home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / contrib / carmel / c-client / carmel2.c < prev    next >
C/C++ Source or Header  |  1994-08-31  |  138KB  |  4,413 lines

  1. #define TESTING
  2. /*----------------------------------------------------------------------
  3.  
  4.     T H E   C A R M E L   M A I L   F I L E   D R I V E R
  5.  
  6.  Author(s):   Laurence Lundblade
  7.               Baha'i World Centre
  8.               Data Processing
  9.               Haifa, Israel
  10.               Internet: lgl@cac.washington.edu or laurence@bwc.org
  11.               September 1992
  12.  
  13.  Last edited: Aug 31, 1994
  14.  
  15. The Carmel2 mail file stores messages in individual files and
  16. implements folders or mailboxes with index files that contain
  17. references to the files a nd a full c-client envelope in an easily
  18. parsed form. It was written as a needed part of the pod mail file
  19. driver with hopes that it might be useful otherwise some day. It has
  20. only been run with the carmel driver.
  21.  
  22. Advantages over Berkeley format and driver:
  23.   + Opening mail folder is very fast
  24.   + Expunge is fast
  25.   + Check point is very fast
  26.   + Memory usage is much lower
  27.   + Search of message headers is fast
  28. Disadvantages:
  29.   - Fetching a large message is slow
  30.   - Searching the message bodies is slow
  31.   - Sorting the mailbox is slow
  32.  
  33.          Index File Format
  34.          -----------------
  35.  
  36. The first line of the file is always:
  37.   "\254\312--CARMEL2-MAIL-FILE-INDEX--\n"
  38.  
  39. It is followed by index entries which are of the format:
  40. ---START-OF-MESSAGE---\007\001nnnnnnnnnn     
  41. Ufrads______________________________
  42. Zxxxxxx
  43. D..... <Many fields here, labeled by the first letter in the line>
  44.        <Fields may be repeated>
  45.  
  46.  
  47. The index is almost an ASCII file. With the first version of this
  48. driver it is not advisable to edit this file, lest the byte counts get
  49. disrupted.  In the future it will be editable. The file starts with
  50. two binary bytes so the file(1) utility will report it as "data" to
  51. discourage would be hackers from messing with it. The ^G is also in
  52. each index entry for the same reason. If you are reading this source you
  53. probably know enough to edit the index file without destroying it.
  54. Other parts of the format are designed for the easiest possible
  55. parsing. The idea was to have a file format that was reasonable to
  56. fiddle for debugging, to discourage inexperienced folks for fiddling
  57. it and to be efficient for use by the program.
  58.  
  59.  
  60.       Routines and data structures
  61.       ----------------------------
  62.  
  63. C-CLIENT INTERFACE FUCTIONS
  64.   carmel2_valid       - check to see if a mailbox is valid for carmel2 mail files
  65.   carmel2_isvalid     - actual work of checking
  66.   carmel2_find        - generate list of carmel2 mailboxes
  67.   carmel2_sift_files  - select mailboxes out of list, used with scandir
  68.   carmel2_find_bboars - dummy routine, doesn't do anything
  69.  
  70.   carmel2_open        - initial phase of opening a mailbox
  71.   carmel2_open2       - real work of opening mailbox, shared with carmel driver
  72.   carmel2_close       - close a mail stream
  73.  
  74.   carmel2_fetchfast   - fetch "fast" message info, noop for this driver
  75.   carmel2_fetchflags  - fetch the flags, a noop for this driver
  76.   carmel2_fetchstructure - fetch and envelope and possibly body  
  77.  
  78.   carmel2_fetchheader - fetch the text header of the message
  79.   carmel2_fetchtext   - fetch the text of the message (no header included)
  80.   carmel2_fetchbody   - fetch the text of a body part of a message
  81.  
  82.   carmel2_setflag     - Set a flag for a message sequence
  83.   carmel2_clearflag   - Clear a flag for a message sequence
  84.   
  85.   carmel2_search      - Invoke the search facilities
  86.  
  87.   carmel2_ping        - Check for new mail and see if still alive
  88.   carmel2_check       - Checkpoint the message statuses to the disk
  89.   carmel2_expunge     - Delete all the messages marked for delete 
  90.   
  91.   carmel2_copy        - Copy a message to another mailbox
  92.   carmel2_move        - Move a message to another mailbox
  93.   
  94.   carmel_gc          - Garbage collection, a noop for this driver
  95.   carmel_cache       - The required c-client cache handler, doesn't do much
  96.  
  97.   carmel2_append      - Append a message to a mail folder
  98.  
  99. SUPPORT FUNCTIONS
  100.   carmel_bodystruct  - Fetch the body structure for a carmel message
  101.   carmel_parse_address - Parse the address out of a carmel index
  102.   carmel_parse_addr_field - Parse individual address field out of carmel index
  103.   carmel2_write_index - Write an entry into a carmel index
  104.   carmel2_index_address - Write an address into a carmel index
  105.  
  106.   carmel_readmsg     - Read a message file into memory, header text or both
  107.   carmel_spool_mail  - Get new mail out of spoold mail file
  108.   carmel_copy_msg_file - Make copy of messsage file when copying (unused)
  109.   
  110.   carmel2_lock        - Lock a carmel index for read or write
  111.   carmel2_unlock      - Unlock a carmel index
  112.   carmel2_update_lock - Touch lock mod time, marking it as active
  113.   carmel2_bezerk_lock - Lock the spool mail file Berkeley style
  114.   carmel2_bezerk_unlock - Unlock the spool mail file
  115.   
  116.   carmel2_check_dir   - Check that directory exists and is writeable 
  117.   carmel2_new_data_file - Get file number for new message file
  118.   carmel2_calc_paths  - Calculate path names for carmel driver
  119.  
  120.   carmel_parse_bezerk_status - Parse the "Status: OR" field in mail headers
  121.   carmel2_getflags    - Turn the named flags into a bit mask
  122.   carmel_new_mc      - Get pointer to new MESSAGECACHE, allocating if needed
  123.  
  124.   carmel_append2     - The real work of append a message to a mailbox
  125.   
  126.   carmel2_search-*    - A bunch of search support routines
  127.  
  128.   month_abbrev2      - Returns three letter month abbreviation given a name
  129.   carmel2_rfc822_date - Parse a date string, into MESSAGECACHE structure
  130.   carmel2_date2string - Generate a date string from MESSAGECACHE
  131.   carmel2_parse_date  - Parse date out of a carmel index
  132.   next_num           - Called to parse dates
  133.  
  134.   strucmp2           - Case insensitive strcmp()
  135.   struncmp2          - Case insensitive strncmp()
  136.   
  137.  ----------------------------------------------------------------------*/
  138.  
  139. #include <stdio.h>
  140. #include <ctype.h>
  141. #include <pwd.h>
  142. #include <netdb.h>
  143. #include <errno.h>
  144. extern int errno;               /* just in case */
  145. #include "mail.h"
  146. #include "osdep.h"
  147. #include <sys/dir.h>
  148. #include <sys/file.h>
  149. #include <sys/stat.h>
  150. #include <sys/time.h>
  151. #include "carmel2.h"
  152. #include "rfc822.h"
  153. #include "misc.h"
  154.  
  155.  
  156. char *strindex2(), *strchr();
  157.  
  158. #ifdef ANSI
  159. static int      carmel2_sift_files(struct direct *);
  160. static BODY    *carmel2_bodystruct(MAILSTREAM *, MESSAGECACHE *);
  161. static ADDRESS *carmel2_parse_address(char *, ADDRESS *, char *);
  162. static char    *carmel2_parse_addr_field(char **);
  163. static int      carmel2_index_address(ADDRESS *, int , FILE *);
  164. static int      carmel2_parse_mail(MAILSTREAM *, long);
  165. void            carmel2_spool_mail(MAILSTREAM *, char *, char *, int);
  166. static int      carmel2_copy_msg_file(MAILSTREAM *, int, char *);
  167. static int      carmel2_bezerk_lock(char *, char *);
  168. static void     carmel2_bezerk_unlock(int, char *);
  169. static int      carmel2_new_data_file(CARMEL2LOCAL *, char *);
  170. static char    *carmel2_calc_paths(int, char *, int);
  171. static short    carmel2_getflags(MAILSTREAM *, char *);
  172. static MESSAGECACHE *carmel2_new_mc(MAILSTREAM *, int);
  173. static char     carmel2_search_all(MAILSTREAM *, long, char *, long);
  174. static char     carmel2_search_answered(MAILSTREAM *, long, char *, long);
  175. static char     carmel2_search_deleted(MAILSTREAM *, long, char *, long);
  176. static char     carmel2_search_flagged(MAILSTREAM *, long, char *, long);
  177. static char     carmel2_search_keyword(MAILSTREAM *, long, char *, long);
  178. static char     carmel2_search_new(MAILSTREAM *, long, char *, long);
  179. static char     carmel2_search_old(MAILSTREAM *, long, char *, long);
  180. static char     carmel2_search_recent(MAILSTREAM *, long, char *, long);
  181. static char     carmel2_search_seen(MAILSTREAM *, long, char *, long);
  182. static char     carmel2_search_unanswered(MAILSTREAM *, long, char *, long);
  183. static char     carmel2_search_undeleted(MAILSTREAM *, long, char *, long);
  184. static char     carmel2_search_unflagged(MAILSTREAM *, long, char *, long);
  185. static char     carmel2_search_unkeyword(MAILSTREAM *, long, char *, long);
  186. static char     carmel2_search_unseen(MAILSTREAM *, long, char *, long);
  187. static char     carmel2_search_before(MAILSTREAM *, long, char *, long);
  188. static char     carmel2_search_on(MAILSTREAM *, long, char *, long);
  189. static char     carmel2_search_since(MAILSTREAM *, long, char *, long);
  190. static unsigned long carmel2_msgdate(MAILSTREAM *, long);
  191. static char     carmel2_search_body(MAILSTREAM *, long, char *, long);
  192. static char     carmel2_search_subject(MAILSTREAM *, long, char *, long);
  193. static char     carmel2_search_text(MAILSTREAM *, long, char *, long);
  194. static char     carmel2_search_bcc(MAILSTREAM *, long, char *, long);
  195. static char     carmel2_search_cc(MAILSTREAM *, long, char *, long);
  196. static char     carmel2_search_from(MAILSTREAM *, long, char *, long);
  197. static char     carmel2_search_to(MAILSTREAM *, long, char *, long);
  198. typedef char (*search_t)  ();
  199. static search_t carmel2_search_date(search_t, long *);
  200. static search_t carmel2_search_flag(search_t, char **);
  201. static search_t carmel2_search_string(search_t, char **, long *);
  202. static void     carmel2_date2string(char *, MESSAGECACHE *);
  203. static void     carmel2_parse_date(MESSAGECACHE *, char *);
  204. static int      next_num(char **);
  205. #else
  206. static int      carmel2_sift_files();
  207. static BODY    *carmel2_bodystruct();
  208. static ADDRESS *carmel2_parse_address();
  209. static char    *carmel2_parse_addr_field();
  210. static int      carmel2_index_address();
  211. static int      carmel2_parse_mail();
  212. void            carmel2_spool_mail();
  213. static int      carmel2_copy_msg_file();
  214. static int      carmel2_bezerk_lock();
  215. static void     carmel2_bezerk_unlock();
  216. static int      carmel2_new_data_file();
  217. static char    *carmel2_calc_paths();
  218. static short    carmel2_getflags();
  219. static MESSAGECACHE *carmel2_new_mc();
  220. static char     carmel2_search_all();
  221. static char     carmel2_search_answered();
  222. static char     carmel2_search_deleted();
  223. static char     carmel2_search_flagged();
  224. static char     carmel2_search_keyword();
  225. static char     carmel2_search_new();
  226. static char     carmel2_search_old();
  227. static char     carmel2_search_recent();
  228. static char     carmel2_search_seen();
  229. static char     carmel2_search_unanswered();
  230. static char     carmel2_search_undeleted();
  231. static char     carmel2_search_unflagged();
  232. static char     carmel2_search_unkeyword();
  233. static char     carmel2_search_unseen();
  234. static char     carmel2_search_before();
  235. static char     carmel2_search_on();
  236. static char     carmel2_search_since();
  237. static unsigned long carmel2_msgdate();
  238. static char     carmel2_search_body();
  239. static char     carmel2_search_subject();
  240. static char     carmel2_search_text();
  241. static char     carmel2_search_bcc();
  242. static char     carmel2_search_cc();
  243. static char     carmel2_search_from();
  244. static char     carmel2_search_to();
  245. typedef char (*search_t)  ();
  246. static search_t carmel2_search_date();
  247. static search_t carmel2_search_flag();
  248. static search_t carmel2_search_string();
  249. static void     carmel2_date2string();
  250. static void     carmel2_parse_date();
  251. static int      next_num();
  252. #endif
  253.  
  254.  
  255. /*------ Driver dispatch used by MAIL -------*/
  256.  
  257. DRIVER carmel2driver = {
  258.   "carmel2",
  259.   (DRIVER *) NIL,               /* next driver */
  260.   carmel2_valid,                /* mailbox is valid for us */
  261.   carmel2_parameters,           /* manipulate parameters */
  262.   carmel2_find,                 /* find mailboxes */
  263.   carmel2_find_bboards,         /* find bboards */
  264.   carmel2_find_all,             /* find all mailboxes */
  265.   carmel2_find_bboards,         /* find all bboards ; just a noop here */
  266.   carmel2_subscribe,            /* subscribe to mailbox */
  267.   carmel2_unsubscribe,          /* unsubscribe from mailbox */
  268.   carmel2_subscribe_bboard,     /* subscribe to bboard */
  269.   carmel2_unsubscribe_bboard,   /* unsubscribe from bboard */
  270.   carmel2_create,
  271.   carmel2_delete,
  272.   carmel2_rename,
  273.   carmel2_open,                 /* open mailbox */
  274.   carmel2_close,                /* close mailbox */
  275.   carmel2_fetchfast,            /* fetch message "fast" attributes */
  276.   carmel2_fetchflags,           /* fetch message flags */
  277.   carmel2_fetchstructure,       /* fetch message envelopes */
  278.   carmel2_fetchheader,          /* fetch message header only */
  279.   carmel2_fetchtext,            /* fetch message body only */
  280.   carmel2_fetchbody,            /* fetch message body section */
  281.   carmel2_setflag,              /* set message flag */
  282.   carmel2_clearflag,            /* clear message flag */
  283.   carmel2_search,               /* search for message based on criteria */
  284.   carmel2_ping,                 /* ping mailbox to see if still alive */
  285.   carmel2_check,                /* check for new messages */
  286.   carmel2_expunge,              /* expunge deleted messages */
  287.   carmel2_copy,                 /* copy messages to another mailbox */
  288.   carmel2_move,                 /* move messages to another mailbox */
  289.   carmel2_append,                /* Append message to a mailbox */
  290.   carmel2_gc,                   /* garbage collect stream */
  291. };
  292.  
  293. MAILSTREAM carmel2proto ={&carmel2driver};/*HACK for default_driver in pine.c*/
  294.  
  295. /*-- Some string constants used in carmel2 indexes --*/
  296. char *carmel2_s_o_m     = "---START-OF-MESSAGE---";
  297. int   carmel2_s_o_m_len = 22;
  298. char *carmel2_s_o_f     =   "\254\312--CARMEL2-MAIL-FILE-INDEX--\n";
  299.  
  300.  
  301.  
  302. /*-- Buffers used for various reasons, also used by carmel driver --*/
  303. char carmel_20k_buf[20000], carmel_path_buf[CARMEL_PATHBUF_SIZE],
  304.      carmel_error_buf[200];
  305.  
  306.  
  307. /*----------------------------------------------------------------------
  308.     Carmel mail validate mailbox
  309.  
  310. Args: - mailbox name
  311.  
  312. Returns: our driver if name is valid, otherwise calls valid in next driver
  313.  ---*/
  314.  
  315. DRIVER *carmel2_valid (name)
  316.         char *name;
  317. {
  318.     return (carmel2_isvalid (name) ? &carmel2driver : NIL);
  319. }
  320.  
  321.  
  322.  
  323. /*----------------------------------------------------------------------
  324.   Open the mailbox and see if it's the correct format
  325.  
  326. Args: name -- name of the mailbox, not fully qualified path
  327.  
  328. Returns: 0 if is is not valid, 1 if it is valid
  329.  
  330. The file must be a regular file and start with the string in the
  331. variable carmel2_s_o_f. It has a magic number of sorts
  332.   ----*/
  333. int
  334. carmel2_isvalid (name)
  335.         char *name;
  336. {
  337.     char           *carmel_index_file;
  338.     struct stat     sbuf;
  339.     int             fd;
  340.     struct carmel_mb_name *parsed_name;
  341.  
  342.     /*---- Must match FQN name of carmel mailboxes ----*/
  343.     parsed_name = carmel_parse_mb_name(name, '2');
  344.     if(parsed_name == NULL)
  345.       return;
  346.     carmel_free_mb_name(parsed_name);
  347.        
  348.     carmel_index_file = carmel2_calc_paths(CalcPathCarmel2Index, name, 0);
  349.     if(carmel_index_file == NULL)
  350.       return(0); /* Will get two error messages here, one from dummy driver */
  351.     
  352.     if(stat(carmel_index_file, &sbuf) < 0)
  353.       return(1); /* If the names match and it doesn't exist, it's valid */
  354.  
  355.     if(!(sbuf.st_mode & S_IFREG))
  356.       return(0);
  357.  
  358.     fd = open(carmel_index_file, O_RDONLY);
  359.     if(fd < 0)
  360.       return(0);
  361.  
  362.     if(read(fd, carmel_20k_buf, 200) <= 0)
  363.       return(0);
  364.  
  365.     close(fd);
  366.  
  367.     if(strncmp(carmel_20k_buf, carmel2_s_o_f, strlen(carmel2_s_o_f)))
  368.        return(0);
  369.  
  370.     return(1);
  371. }
  372.  
  373.  
  374.  
  375. /*----------------------------------------------------------------------
  376.    Set parameters for the carmel driver.
  377.  
  378.    Currently does nothing
  379.   ----*/
  380. void *
  381. carmel2_parameters()
  382. {
  383. }
  384.  
  385.  
  386.  
  387.  
  388. /*----------------------------------------------------------------------
  389.    Carmel mail find list of mailboxes
  390.  
  391. Args:  stream -- mail stream to find mailboxes for
  392.        pat    -- wildcard pattern to match (currently unused)
  393.  
  394. Returns nothing, the results are passed back by calls to mm_log
  395.  
  396. This needs testing
  397.  ----*/
  398.  
  399. void 
  400. carmel2_find (stream, pat)
  401.     MAILSTREAM *stream;
  402.     char *pat;
  403. {
  404.     char                   tmp[MAILTMPLEN];
  405.     struct direct        **namelist, **n;
  406.     int                    num;
  407.     extern int             alphasort();
  408.     struct carmel_mb_name *parsed_name;
  409.     struct passwd         *pw;
  410.  
  411.     parsed_name = carmel_parse_mb_name(pat, '2');
  412.     if(parsed_name == NULL)
  413.       return;
  414.  
  415.     if(parsed_name->user == NULL) {
  416.         sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
  417.     } else {
  418.         pw = getpwnam(parsed_name->user);
  419.         if(pw == NULL) {
  420.             sprintf(carmel_error_buf,
  421.                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
  422.                     pat, parsed_name->user);
  423.             mm_log(carmel_error_buf, ERROR);
  424.             return;
  425.         }
  426.         sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL2_INDEX_DIR);
  427.     }
  428.  
  429.     sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
  430.  
  431.     num = scandir(tmp, &namelist, carmel2_sift_files, alphasort);
  432.  
  433.     if(num <= 0) {
  434. /*  Seems this error message should not be logged
  435.         sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
  436.                 pat, strerror(errno));
  437.         mm_log(carmel_error_buf, ERROR); */
  438.         return;
  439.     }
  440.  
  441.     for(n = namelist; num > 0; num--, n++) {
  442.         if(parsed_name->user == NULL) {
  443.             sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
  444.                     CARMEL_NAME_CHAR, (*n)->d_name);
  445.         } else {
  446.             sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
  447.                     parsed_name->version, CARMEL_NAME_CHAR,
  448.                     parsed_name->user, CARMEL_NAME_CHAR,
  449.                     (*n)->d_name);
  450.         }
  451.         mm_mailbox(tmp);
  452.         free(*n); 
  453.     }
  454.     free(namelist);   
  455.     carmel_free_mb_name(parsed_name);
  456. }
  457.  
  458.  
  459.  
  460. /*----------------------------------------------------------------------
  461.    Find_all is the same as find for the carmel2 format -- no notion
  462.    of subscribed and unsubscribed
  463.   ---*/
  464. void 
  465. carmel2_find_all (stream, pat)
  466.     MAILSTREAM *stream;
  467.     char *pat;
  468. {
  469.   carmel2_find(stream, pat);
  470. }
  471.  
  472.  
  473.  
  474. /*----------------------------------------------------------------------
  475.   Carmel2 mail find list of bboards; always NULL, no bboards 
  476.  ----*/
  477. void carmel2_find_bboards (stream,pat)
  478.         MAILSTREAM *stream;
  479.         char *pat;
  480. {
  481.   /* Always a no-op, Carmel2 file format doesn't do news */
  482. }
  483.  
  484.  
  485.  
  486. /*----------------------------------------------------------------------
  487.   Carmel2 mail find list of bboards; always NULL, no bboards 
  488.  ----*/
  489. void carmel2_find_all_bboards (stream,pat)
  490.         MAILSTREAM *stream;
  491.         char *pat;
  492. {
  493.   /* Always a no-op, Carmel2 file format doesn't do news */
  494. }
  495.  
  496.  
  497.  
  498. /*----------------------------------------------------------------------
  499.    This is used by scandir to determine which files in the directory
  500.  are treated as mail files
  501.   ----*/
  502. static int
  503. carmel2_sift_files(dir)
  504.      struct direct *dir;
  505. {
  506.     if(dir->d_name[0] == '.')
  507.       return(0);
  508.     else
  509.       return(1);
  510. }
  511.  
  512.  
  513.  
  514. /*----------------------------------------------------------------------
  515.     Dummy subscribe/unsubscribe routines.  
  516.     Carmel driver does support this. Maybe it should support the UNIX
  517.     sm sometime
  518.   ----*/
  519. long carmel2_subscribe() {}
  520. long carmel2_unsubscribe() {}
  521. long carmel2_subscribe_bboard() {}
  522. long carmel2_unsubscribe_bboard() {}
  523.  
  524.  
  525.  
  526. /*----------------------------------------------------------------------
  527.   ----*/
  528. long
  529. carmel2_create(stream, mailbox)
  530.      MAILSTREAM *stream;
  531.      char       *mailbox;
  532. {
  533. }
  534.  
  535.  
  536.  
  537. /*----------------------------------------------------------------------
  538.   ----*/
  539. long
  540. carmel2_delete(stream, mailbox)
  541.      MAILSTREAM *stream;
  542.      char       *mailbox;
  543. {
  544. }
  545.  
  546.  
  547.  
  548. /*----------------------------------------------------------------------
  549.   ----*/
  550. long
  551. carmel2_rename(stream, orig_mailbox, new_mailbox)
  552.      MAILSTREAM *stream;
  553.      char       *orig_mailbox, *new_mailbox;
  554. {
  555. }
  556.  
  557.  
  558. /*----------------------------------------------------------------------
  559.   Open a carmel2 mail folder/stream
  560.  
  561. Args: stream -- stream to by fully opened
  562.  
  563. Returns: it's argument or NULL of the open fails
  564.  
  565. This needs testing and more code, see pod_open for examples
  566.   ----*/
  567.  
  568. MAILSTREAM *
  569. carmel2_open (stream)
  570.         MAILSTREAM *stream;
  571. {
  572.     char tmp[MAILTMPLEN];
  573.     struct hostent *host_name;
  574.  
  575.     if(!stream) return &carmel2proto; /* Must be a OP_PROTOTYPE call */
  576.  
  577.     /* close old file if stream being recycled */
  578.     if (LOCAL) {
  579.         carmel2_close (stream);         /* dump and save the changes */
  580.         stream->dtb = &carmel2driver;   /* reattach this driver */
  581.         mail_free_cache (stream);       /* clean up cache */
  582.     }
  583.  
  584.     mailcache = carmel2_cache;
  585.  
  586.     /* Allocate local stream */
  587.     stream->local = fs_get (sizeof (CARMEL2LOCAL));
  588.  
  589.     stream->readonly = 1; /* Read-only till more work is done */
  590.  
  591.     LOCAL->msg_buf             = NULL;
  592.     LOCAL->msg_buf_size        = 0;
  593.     LOCAL->buffered_file       = NULL;
  594.     LOCAL->msg_buf_text_start  = NULL;
  595.     LOCAL->msg_buf_text_offset = 0;
  596.     LOCAL->stdio_buf           = NULL;
  597.     stream->msgno              = -1;
  598.     stream->env                = NULL;
  599.     stream->body               = NULL;
  600.     stream->scache             = 1;
  601.     LOCAL->calc_paths          = carmel2_calc_paths;
  602.     LOCAL->aux_copy            = NULL;
  603.     LOCAL->new_file_on_copy    = 1;
  604.  
  605.     if(carmel_open2(stream,
  606.        (*(LOCAL->calc_paths))(CalcPathCarmel2Index, stream->mailbox,0)) < 0)
  607.       return(NULL);
  608.  
  609.     mail_exists (stream,stream->nmsgs);
  610.     mail_recent (stream,stream->recent);
  611.  
  612.     return(stream);
  613. }
  614.  
  615.  
  616.  
  617. /*----------------------------------------------------------------------
  618.     Do the real work of opening a Carmel folder. 
  619.  
  620. Args: stream -- The mail stream being opened
  621.       file   -- Path name of the actual Carmel index file
  622.  
  623. Returns: -1 if the open fails
  624.           0 if it succeeds
  625.  
  626. This is shared between the Carmel driver and the Pod driver.
  627.  
  628. Here, the status, size and date (fast info) of a message entry
  629. is read out of the index file into the MESSAGECACHE structures.
  630. To make the reading efficient these items are at the beginning 
  631. of each entry and there is a byte offset to the next entry.
  632. If the byte offset is wrong (as detected by looking for the
  633. start of message string) then the index is read line by line
  634. until it synchs up. This allows manual editing of the index.
  635. However, the first two lines of an index entry cannot be
  636. edited because mail_check() writes them in place. If these
  637. lines have been edited it is detected here and the folder is
  638. deemed corrupt.
  639.   ---*/
  640. carmel_open2(stream, file)
  641.      MAILSTREAM *stream;
  642.      char       *file;
  643. {
  644.     long          file_pos, recent;
  645.     MESSAGECACHE *mc;
  646.     struct stat   sb;
  647.  
  648.     if(stat(file, &sb) < 0) {
  649.         sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
  650.         mm_log(carmel_error_buf, ERROR);
  651.         return(-1);
  652.     }
  653.  
  654.     LOCAL->index_stream = fopen(file, stream->readonly ? "r" : "r+");
  655.     LOCAL->stdio_buf    = fs_get(CARMEL2_INDEX_BUF_SIZE);
  656.     setbuffer(LOCAL->index_stream, LOCAL->stdio_buf, CARMEL2_INDEX_BUF_SIZE);
  657.     if(LOCAL->index_stream == NULL) {
  658.         sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
  659.         mm_log(carmel_error_buf, ERROR);
  660.         return(-1);
  661.     }
  662.  
  663.     recent            = 0;
  664.     stream->nmsgs     = 0;
  665.     LOCAL->cache_size = 0;
  666.     LOCAL->mc_blocks  = NULL;
  667.     LOCAL->index_size = sb.st_size;
  668.  
  669.     /*---- Read line with magic number, which we already checked ------*/
  670.     if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL){
  671.         fclose(LOCAL->index_stream);
  672.         mm_log("Mailbox is corrupt. Open failed", ERROR);
  673.         return(-1);
  674.     }
  675.  
  676.     file_pos = ftell(LOCAL->index_stream);  
  677.     if(carmel2_parse_mail(stream, file_pos) < 0) {
  678.         fclose(LOCAL->index_stream);
  679.         mm_log("Mailbox is corrupt. Open failed", ERROR);
  680.         return(-1);
  681.     }
  682.  
  683.     /* Bug, this doesn't really do the recent correctly */
  684.  
  685.     stream->recent    = recent;
  686.     return(0);
  687. }
  688.  
  689.  
  690.  
  691. /*----------------------------------------------------------------------
  692.      Carmel mail close
  693.  
  694. Args: stream -- stream to close
  695.  
  696.  ----*/
  697.  
  698. void
  699. carmel2_close (stream)
  700.         MAILSTREAM *stream;
  701. {
  702.     if (LOCAL) {                        /* only if a file is open */
  703.         carmel2_check (stream);         /* dump final checkpoint */
  704.         if(LOCAL->msg_buf)
  705.           fs_give ((void **) &LOCAL->msg_buf);
  706.         if(LOCAL->stdio_buf)
  707.           fs_give ((void **) &LOCAL->stdio_buf);
  708.                                     /* nuke the local data */
  709.         fs_give ((void **) &stream->local);
  710.         stream->dtb = NIL;              /* log out the DTB */
  711.     }
  712. }
  713.  
  714.  
  715.  
  716. /*----------------------------------------------------------------------
  717.     Carmel mail fetch fast information.
  718.  
  719. This is a no-op because the data is always available as it is read in to
  720. the message cache blocks when the folder is opened
  721. ----*/
  722. void
  723. carmel2_fetchfast (stream, sequence)
  724.         MAILSTREAM *stream;
  725.         char *sequence;
  726. {
  727.     return;
  728. }
  729.  
  730.  
  731.  
  732. /*----------------------------------------------------------------------
  733.     Carmel2 mail fetch flags.
  734.  
  735. This is a no-op because the data is always available as it is read in to
  736. the message cache blocks when the folder is opened
  737. ----*/
  738. void
  739. carmel2_fetchflags(stream, sequence)
  740.         MAILSTREAM *stream;
  741.         char *sequence;
  742. {
  743.     return;                     /* no-op for local mail */
  744. }
  745.  
  746.  
  747.  
  748. /*----------------------------------------------------------------------
  749.   Carmel mail fetch message structure
  750.  Args: stream -- stream to get structure for
  751.        msgno  -- Message number to fetch data for
  752.        body   -- Pointer to place to return body strucuture, may be NULL
  753.  
  754. If the request is the for the same msgno as last time, the saved copy
  755. of the envelope and/or body structure is returned.
  756.  
  757. To get the envelope the Carmel index file itself must be read and parsed,
  758. but this is fast because it is easy to parse (by design) and the amount of
  759. data is small.
  760.  
  761. To get the body, the whole message is read into memory and then parsed
  762. by routines called from here. Doing this for a large message can be slow,
  763. so it is best not to request the body if it is not needed. (body == NULL)
  764.  ----*/
  765.  
  766. ENVELOPE *
  767. carmel2_fetchstructure (stream, msgno, body)
  768.         MAILSTREAM *stream;
  769.         long        msgno;
  770.         BODY      **body;
  771. {
  772.     MESSAGECACHE *mc;
  773.     ENVELOPE    **env;
  774.     BODY        **b;
  775.   
  776.     env = &stream->env;         
  777.     b   = &stream->body;
  778.  
  779.     if (msgno != stream->msgno){
  780.         /* flush old poop if a different message */
  781.         mail_free_envelope (env);
  782.         mail_free_body (b);
  783.     }
  784.     stream->msgno = msgno;
  785.  
  786.     mc = MC(msgno);
  787.  
  788.     if(*env == NULL) {
  789.         *env = mail_newenvelope();
  790.       
  791.         fseek(LOCAL->index_stream, mc->data1, 0);
  792.         fgets(carmel_20k_buf, sizeof(carmel_20k_buf), LOCAL->index_stream);
  793.         if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
  794.           return(NULL); /* Oh ooo */
  795.       
  796.          carmel2_date2string(carmel_20k_buf, mc);
  797.          (*env)->date = cpystr(carmel_20k_buf);
  798.       
  799.          while(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
  800.                      LOCAL->index_stream) != NULL &&
  801.                strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len)) {
  802.              carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
  803.              switch(*carmel_20k_buf) {
  804.                case 'F':
  805.                  (*env)->from = carmel2_parse_address(carmel_20k_buf,
  806.                                                      (*env)->from,
  807.                                                      mylocalhost());
  808.                  break;
  809.                case 'T':
  810.                  (*env)->to = carmel2_parse_address(carmel_20k_buf,
  811.                                                    (*env)->to,
  812.                                                    mylocalhost());
  813.                  break;
  814.                case 'C':
  815.                  (*env)->cc = carmel2_parse_address(carmel_20k_buf,
  816.                                                    (*env)->cc,
  817.                                                    mylocalhost());
  818.                  break;
  819.                case 'B':
  820.                  (*env)->bcc = carmel2_parse_address(carmel_20k_buf,
  821.                                                     (*env)->bcc,
  822.                                                     mylocalhost());
  823.                  break;
  824.                case 'E':
  825.                  (*env)->sender = carmel2_parse_address(carmel_20k_buf,
  826.                                                        (*env)->sender,
  827.                                                        mylocalhost());
  828.                  break;
  829.                case 'R':
  830.                  (*env)->reply_to = carmel2_parse_address(carmel_20k_buf,
  831.                                                          (*env)->reply_to,
  832.                                                          mylocalhost());
  833.                  break;
  834.                case 'H':
  835.                  (*env)->return_path =carmel2_parse_address(carmel_20k_buf,
  836.                                                            (*env)->return_path,
  837.                                                            mylocalhost());
  838.                  break;
  839.                case 'J':
  840.                  (*env)->subject     = cpystr(carmel_20k_buf+1);
  841.                  break;
  842.                case 'I':
  843.                  (*env)->message_id  = cpystr(carmel_20k_buf+1);
  844.                  break;
  845.                case 'L':
  846.                  (*env)->in_reply_to = cpystr(carmel_20k_buf+1);
  847.                  break;
  848.                case 'N':
  849.                  (*env)->newsgroups  = cpystr(carmel_20k_buf+1);
  850.                  break;
  851.                case 'r':
  852.                  (*env)->remail      = cpystr(carmel_20k_buf+1);
  853.                  break;
  854.                default:
  855.                  break;
  856.              }
  857.          }
  858.     }
  859.  
  860.     if(body != NULL) {
  861.         if(*b == NULL) {
  862.             /* Have to fetch the body structure too */
  863.             *b  = carmel2_bodystruct(stream, mc);
  864.         }
  865.         *body = *b;
  866.     }
  867.   
  868.     return(*env);    /* return the envelope */
  869. }
  870.  
  871.  
  872.  
  873.  
  874. /*----------------------------------------------------------------------
  875.   Carmel mail fetch message header
  876.  
  877. Args: stream -- 
  878.       msgno
  879.  
  880. Returns: pointer to text of mail header. Returned string is null terminated
  881.          and consists of internel storage. It will be valid till the next
  882.          call to mail_fetchheader() or mail_fetchtext(). An empty
  883.          string is returned if the fetch fails.
  884.  ----*/
  885.  
  886. char *
  887. carmel2_fetchheader (stream, msgno)
  888.         MAILSTREAM *stream;
  889.         long msgno;
  890. {
  891.     char          *header;
  892.     MESSAGECACHE  *mc;
  893.  
  894.     mc     = MC(msgno);
  895.     header = carmel2_readmsg(stream, 1, 0, mc->data2);
  896.  
  897.     return(header == NULL ? "" : header);
  898. }
  899.  
  900.  
  901.  
  902. /*----------------------------------------------------------------------
  903.   Carmel mail fetch message text (only)
  904.  
  905. Args: stream -- 
  906.       msgno
  907.  
  908. Returns: pointer to text of mail message. Returned string is null terminated
  909.          and consists of internel storage. It will be valid till the next
  910.          call to mail_fetchheader() or mail_fetchtext(). An empty
  911.          string is returned if the fetch fails.
  912.  ----*/
  913.  
  914. char *
  915. carmel2_fetchtext(stream, msgno)
  916.         MAILSTREAM *stream;
  917.         long msgno;
  918. {
  919.     MESSAGECACHE *mc;
  920.     char         *m;
  921.  
  922.     mc = MC(msgno);
  923.  
  924.     if (!mc->seen) {            /* if message not seen before */
  925.         mc->seen = T;           /* mark as seen */
  926.         LOCAL->dirty = T;       /* and that stream is now dirty */
  927.     }
  928.  
  929.     m = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
  930.  
  931.     return(m);
  932. }
  933.  
  934.  
  935.  
  936. /*----------------------------------------------------------------------
  937.     Fetch MIME body parts
  938.  
  939. Args: stream
  940.       msgno
  941.       section -- string section number spec. i.e. "1.3.4"
  942.       len     -- pointer to return len in
  943.  
  944. Returns: The contents of the body part, or NULL
  945.  ---*/
  946.  
  947. char *
  948. carmel2_fetchbody (stream, msgno, section, len)
  949.         MAILSTREAM    *stream;
  950.         long           msgno;
  951.         char          *section;
  952.         unsigned long *len;
  953. {
  954.     char         *base;
  955.     BODY         *b;
  956.     PART         *part;
  957.     int           part_num;
  958.     long          offset;
  959.     MESSAGECACHE *mc;
  960.  
  961.     if (carmel2_fetchstructure(stream, msgno, &b) == NULL || b == NULL)
  962.       return(NULL);
  963.  
  964.     if(section == NULL || *section == '\0')
  965.       return(NULL);
  966.  
  967.     part_num = strtol(section, §ion, 10);
  968.     if(part_num <= 0)
  969.       return(NULL);
  970.  
  971.     base   = carmel2_fetchtext(stream, msgno);
  972.     if(base == NULL)
  973.       base = "";
  974.     offset = 0;
  975.  
  976.     do {                         /* until find desired body part */
  977.         if (b->type == TYPEMULTIPART) {
  978.             part = b->contents.part;    /* yes, find desired part */
  979.             while (--part_num && (part = part->next));
  980.             if (!part) 
  981.               return (NIL);     /* bad specifier */
  982.  
  983.             /* Found part, get ready to go further */
  984.             b = &part->body;
  985.             if(b->type == TYPEMULTIPART && *section == '\0')
  986.               return(NULL); /* Ran out of section numbers, needed more */
  987.                
  988.             offset = part->offset;      /* and its offset */
  989.  
  990.         } else if (part_num != 1) {
  991.             return NIL; /* Not Multipart, better be part 1 */
  992.         }
  993.  
  994.                                     /* need to go down further? */
  995.  
  996.         if(*section == 0) {
  997.             break;
  998.         } else { 
  999.             switch (b->type) {
  1000.               case TYPEMESSAGE: /* embedded message, calculate new base */
  1001.                 offset = b->contents.msg.offset;
  1002.                 b = b->contents.msg.body;
  1003.                 /* got its body, drop into multipart case*/
  1004.   
  1005.               case TYPEMULTIPART:            /* multipart, get next section */
  1006.                 if ((*section++ == '.') &&
  1007.                     (part_num = strtol (section, §ion,10)) > 0)
  1008.                   break; /* Found the part */
  1009.   
  1010.               default:                  /* bogus subpart specification */
  1011.                 return(NULL);
  1012.             }
  1013.         }
  1014.     } while (part_num);
  1015.  
  1016.     mc = MC(msgno);
  1017.                                   /* lose if body bogus */
  1018.     if ((!b) || b->type == TYPEMULTIPART)
  1019.       return(NULL);
  1020.  
  1021.     if (!mc->seen) {            /* if message not seen before */
  1022.       mc->seen = T;             /* mark as seen */
  1023.       LOCAL->dirty = T;         /* and that stream is now dirty */
  1024.     }
  1025.  
  1026.     *len = b->size.ibytes;
  1027.     return(base + offset);
  1028. }
  1029.  
  1030.  
  1031.  
  1032. /*----------------------------------------------------------------------
  1033.  *  Carmel mail set flag
  1034.  * Accepts: MAIL stream
  1035.  *          sequence
  1036.  *          flag(s)
  1037.  */
  1038.  
  1039. void
  1040. carmel2_setflag (stream,sequence,flag)
  1041.         MAILSTREAM *stream;
  1042.         char *sequence;
  1043.         char *flag;
  1044. {
  1045.   MESSAGECACHE *elt;
  1046.   long i;
  1047.   short f = carmel2_getflags (stream,flag);
  1048.   if (!f) return;               /* no-op if no flags to modify */
  1049.                                 /* get sequence and loop on it */
  1050.   if (mail_sequence (stream,sequence))
  1051.     for (i = 1; i <= stream->nmsgs; i++){
  1052.         elt = MC(i);
  1053.         if (elt->sequence) {
  1054.                                     /* set all requested flags */
  1055.           if (f&fSEEN) elt->seen = T;
  1056.           if (f&fDELETED) elt->deleted = T;
  1057.           if (f&fFLAGGED) elt->flagged = T;
  1058.           if (f&fANSWERED) elt->answered = T;
  1059.           /* Could be more creative about keeping track to see if something
  1060.              really changed */
  1061.           LOCAL->dirty = T;
  1062.         }
  1063.     }
  1064. }
  1065.  
  1066.  
  1067.  
  1068. /*----------------------------------------------------------------------
  1069.  * Carmel mail clear flag
  1070.  * Accepts: MAIL stream
  1071.  *          sequence
  1072.  *          flag(s)
  1073.  */
  1074.  
  1075. void
  1076. carmel2_clearflag (stream,sequence,flag)
  1077.         MAILSTREAM *stream;
  1078.         char *sequence;
  1079.         char *flag;
  1080. {
  1081.   MESSAGECACHE *elt;
  1082.   long i = stream->nmsgs;
  1083.   short f = carmel2_getflags (stream,flag);
  1084.   if (!f) return;               /* no-op if no flags to modify */
  1085.                                 /* get sequence and loop on it */
  1086.   if (mail_sequence (stream,sequence))
  1087.     for(i = 1; i <= stream->nmsgs; i++) {
  1088.         elt = MC(i);
  1089.         if (elt->sequence) {
  1090.                                     /* clear all requested flags */
  1091.           if (f&fSEEN) elt->seen = NIL;
  1092.           if (f&fDELETED) elt->deleted = NIL;
  1093.           if (f&fFLAGGED) elt->flagged = NIL;
  1094.           if (f&fANSWERED) elt->answered = NIL;
  1095.                             /* clearing either seen or deleted does this */
  1096.           /* Could be more creative about keeping track to see if something
  1097.              really changed */
  1098.           LOCAL->dirty = T;     /* mark stream as dirty */
  1099.         }
  1100.     }
  1101. }
  1102.  
  1103.  
  1104.  
  1105. /* Carmel mail search for messages
  1106.  * Accepts: MAIL stream
  1107.  *          search criteria
  1108.  */
  1109.  
  1110. void 
  1111. carmel2_search (stream,criteria)
  1112.         MAILSTREAM *stream;
  1113.         char *criteria;
  1114. {
  1115.   long i,n;
  1116.   char *d;
  1117.   search_t f;
  1118.  
  1119.                                 /* initially all searched */
  1120.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  1121.                                 /* get first criterion */
  1122.   if (criteria && (criteria = strtok (criteria," "))) {
  1123.                                 /* for each criterion */
  1124.     for (; criteria; (criteria = strtok (NIL," "))) {
  1125.       f = NIL; d = NIL; n = 0;  /* init then scan the criterion */
  1126.       switch (*ucase (criteria)) {
  1127.       case 'A':                 /* possible ALL, ANSWERED */
  1128.         if (!strcmp (criteria+1,"LL")) f = carmel2_search_all;
  1129.         else if (!strcmp (criteria+1,"NSWERED")) f = carmel2_search_answered;
  1130.         break;
  1131.       case 'B':                 /* possible BCC, BEFORE, BODY */
  1132.         if (!strcmp (criteria+1,"CC"))
  1133.           f = carmel2_search_string (carmel2_search_bcc,&d,&n);
  1134.         else if (!strcmp (criteria+1,"EFORE"))
  1135.           f = carmel2_search_date (carmel2_search_before,&n);
  1136.         else if (!strcmp (criteria+1,"ODY"))
  1137.           f = carmel2_search_string (carmel2_search_body,&d,&n);
  1138.         break;
  1139.       case 'C':                 /* possible CC */
  1140.         if (!strcmp (criteria+1,"C"))
  1141.           f = carmel2_search_string (carmel2_search_cc,&d,&n);
  1142.         break;
  1143.       case 'D':                 /* possible DELETED */
  1144.         if (!strcmp (criteria+1,"ELETED")) f = carmel2_search_deleted;
  1145.         break;
  1146.       case 'F':                 /* possible FLAGGED, FROM */
  1147.         if (!strcmp (criteria+1,"LAGGED")) f = carmel2_search_flagged;
  1148.         else if (!strcmp (criteria+1,"ROM"))
  1149.           f = carmel2_search_string (carmel2_search_from,&d,&n);
  1150.         break;
  1151.       case 'K':                 /* possible KEYWORD */
  1152.         if (!strcmp (criteria+1,"EYWORD"))
  1153.           f = carmel2_search_flag (carmel2_search_keyword,&d);
  1154.         break;
  1155.       case 'N':                 /* possible NEW */
  1156.         if (!strcmp (criteria+1,"EW")) f = carmel2_search_new;
  1157.         break;
  1158.  
  1159.       case 'O':                 /* possible OLD, ON */
  1160.         if (!strcmp (criteria+1,"LD")) f = carmel2_search_old;
  1161.         else if (!strcmp (criteria+1,"N"))
  1162.           f = carmel2_search_date (carmel2_search_on,&n);
  1163.         break;
  1164.       case 'R':                 /* possible RECENT */
  1165.         if (!strcmp (criteria+1,"ECENT")) f = carmel2_search_recent;
  1166.         break;
  1167.       case 'S':                 /* possible SEEN, SINCE, SUBJECT */
  1168.         if (!strcmp (criteria+1,"EEN")) f = carmel2_search_seen;
  1169.         else if (!strcmp (criteria+1,"INCE"))
  1170.           f = carmel2_search_date (carmel2_search_since,&n);
  1171.         else if (!strcmp (criteria+1,"UBJECT"))
  1172.           f = carmel2_search_string (carmel2_search_subject,&d,&n);
  1173.         break;
  1174.       case 'T':                 /* possible TEXT, TO */
  1175.         if (!strcmp (criteria+1,"EXT"))
  1176.           f = carmel2_search_string (carmel2_search_text,&d,&n);
  1177.         else if (!strcmp (criteria+1,"O"))
  1178.           f = carmel2_search_string (carmel2_search_to,&d,&n);
  1179.         break;
  1180.       case 'U':                 /* possible UN* */
  1181.         if (criteria[1] == 'N') {
  1182.           if (!strcmp (criteria+2,"ANSWERED")) f = carmel2_search_unanswered;
  1183.           else if (!strcmp (criteria+2,"DELETED")) f = carmel2_search_undeleted;
  1184.           else if (!strcmp (criteria+2,"FLAGGED")) f = carmel2_search_unflagged;
  1185.           else if (!strcmp (criteria+2,"KEYWORD"))
  1186.             f = carmel2_search_flag (carmel2_search_unkeyword,&d);
  1187.           else if (!strcmp (criteria+2,"SEEN")) f = carmel2_search_unseen;
  1188.         }
  1189.         break;
  1190.       default:                  /* we will barf below */
  1191.         break;
  1192.       }
  1193.       if (!f) {                 /* if can't determine any criteria */
  1194.         sprintf(carmel_error_buf,"Unknown search criterion: %s",criteria);
  1195.         mm_log (carmel_error_buf,ERROR);
  1196.         return;
  1197.       }
  1198.                                 /* run the search criterion */
  1199.       for (i = 1; i <= stream->nmsgs; ++i)
  1200.         if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  1201.           mail_elt (stream,i)->searched = NIL;
  1202.     }
  1203.                                 /* report search results to main program */
  1204.     for (i = 1; i <= stream->nmsgs; ++i)
  1205.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  1206.   }
  1207. }
  1208.  
  1209.  
  1210.  
  1211.  
  1212. /*----------------------------------------------------------------------
  1213.  * Carmel mail ping mailbox
  1214.  * Accepts: MAIL stream
  1215.  * Returns: T if stream alive, else NIL
  1216.  */
  1217.  
  1218. long
  1219. carmel2_ping (stream)
  1220.     MAILSTREAM *stream;
  1221. {
  1222.     struct stat sb;
  1223.     char path[1000], *mailfile;
  1224. #ifdef TESTING
  1225.     char debug_buf[1000];
  1226. #endif
  1227.  
  1228.     if(!stream->readonly &&
  1229.        carmel2_update_lock(stream->local, stream->mailbox, READ_LOCK) < 0) {
  1230.         /* Yuck! Someone messed up our lock file, this stream is hosed */
  1231.         mm_log("Mailbox updated unexpectedly! Mailbox closed", ERROR);
  1232.         stream->readonly = 1;
  1233.         return NIL;
  1234.     }
  1235.  
  1236.     /*--- First check to see if the Carmel index grew ----*/
  1237.     /* BUG, will this really work on NFS since the file is held open? */
  1238.     stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0), &sb);
  1239.     if(sb.st_size > LOCAL->index_size) {
  1240. #ifdef TESTING
  1241.         mm_log("!!!!! Carmel index grew", NIL);
  1242. #endif
  1243.         /* Pull in the new mail.... */
  1244. #ifdef MAY_NEED_FOR_NFS
  1245.         /*---- First close and reopen the mail file -----*/
  1246.         fclose(LOCAL->index_stream);
  1247.         LOCAL->index_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Index,
  1248.                                                          stream->mailbox, 0),
  1249.                                     stream->readonly ? "r" : "r+");
  1250.         if(LOCAL->index_stream == NULL) {
  1251.             mm_log("Mailbox stolen. Mailbox closed", ERROR);
  1252.             stream->readonly = 1;
  1253.             return NIL;
  1254.         } 
  1255. #endif
  1256.         if(carmel2_parse_mail(stream, LOCAL->index_size) < 0) {
  1257.             mm_log("Mailbox corrupted. Mailbox closed", ERROR);
  1258.             stream->readonly = 1;
  1259.             return NIL;
  1260.         }
  1261.         LOCAL->index_size = sb.st_size;
  1262.     }
  1263.  
  1264.     if(sb.st_size < LOCAL->index_size) {
  1265.         /* Something bad happened, mail box shrunk on us, shutdown */
  1266.         stream->readonly = 1;
  1267.         mm_log("Mailbox shrank unexpectedly! Mailbox closed", ERROR);
  1268.         return NIL;
  1269.     }
  1270.  
  1271. #ifdef TESTING
  1272.     sprintf(debug_buf, "!!!!! Mailbox:\"%s\"  pretty_mailbox:\"%s\"",
  1273.             stream->mailbox, carmel_pretty_mailbox(stream->mailbox));
  1274.     mm_log(debug_buf, NIL);
  1275.     sprintf(debug_buf, "!!!! Readonly:%d, Carmel:%d", stream->readonly,
  1276.             LOCAL->carmel);
  1277.     mm_log(debug_buf, NIL);
  1278. #endif    
  1279.  
  1280.     if(!stream->readonly &&
  1281.        ((LOCAL->carmel &&
  1282.          strcmp(carmel_pretty_mailbox(stream->mailbox), "MAIL") == 0) ||
  1283.         strucmp2(carmel_pretty_mailbox(stream->mailbox), "inbox") == 0)) {
  1284.         /* If it's the inbox we've got to check the spool file */
  1285.         mailfile = getenv("MAIL") == NULL ? MAILFILE : getenv("MAIL");
  1286.         sprintf(path, mailfile, myhomedir());
  1287. #ifdef TESTING
  1288.         sprintf(debug_buf, "!!!!! Checking spool mail\"%s\"", path);
  1289.         mm_log(debug_buf, NIL);
  1290. #endif    
  1291.         if(stat(path, &sb) < 0)
  1292.           return(T);  /* No new mail...*/
  1293.         if(sb.st_size > 0) {
  1294.             mm_critical(stream);
  1295.             if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
  1296.                 mm_nocritical(stream);
  1297.                 return(T);
  1298.             }
  1299.             mm_log("!!!! Inbox locked, sucking in mail", NIL);
  1300.             carmel2_spool_mail(stream, path, stream->mailbox, 1); /*go get it*/
  1301.             carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
  1302.             mm_nocritical(stream);
  1303.             stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox,0),
  1304.                  &sb);
  1305.             LOCAL->index_size = sb.st_size;
  1306.         }
  1307.     } 
  1308.     
  1309.     return T;                   
  1310. }
  1311.  
  1312.  
  1313.  
  1314.  
  1315.  
  1316. /*----------------------------------------------------------------------
  1317.     This checks for new mail and checkpoints the mail file
  1318.  
  1319. Args -- The stream to check point
  1320.  
  1321.  ----*/
  1322. void 
  1323. carmel2_check(stream)
  1324.         MAILSTREAM *stream;
  1325. {
  1326.     (void)carmel2_check2(stream);
  1327. }
  1328.  
  1329.  
  1330.  
  1331. /*----------------------------------------------------------------------
  1332.     Do actual work of a check on carmel2 index, returning status
  1333.  
  1334. Returns: 0 if no checkpoint was done, 1 if it was done.
  1335.   ----*/
  1336. carmel2_check2(stream)
  1337.      MAILSTREAM *stream;
  1338. {
  1339.     int msgno;
  1340.     MESSAGECACHE *mc;
  1341.  
  1342.     if(stream->readonly || LOCAL == NULL)
  1343.       return(0); /* Nothing to do in readonly or closed case */
  1344.  
  1345.     carmel2_ping(stream); /* check for new mail (Ping always checks) */
  1346.  
  1347.     if(!LOCAL->dirty)
  1348.       return(0); /* Nothing to do */
  1349.  
  1350.     mm_critical(stream);
  1351.     if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
  1352.         mm_nocritical(stream);
  1353.         return(0); /* Couldn't get a write lock, nothing we can do */
  1354.     }
  1355.  
  1356.     for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
  1357.         mc = MC(msgno);
  1358.         fseek(LOCAL->index_stream, mc->data1 +  carmel2_s_o_m_len  + 13, 0);
  1359.         if(fprintf(LOCAL->index_stream,
  1360.                    "U%c%c%c%c%c____________________________\n",
  1361.                    mc->flagged  ? 'F' : 'f',
  1362.                    mc->recent   ? 'R' : 'r',
  1363.                    mc->answered ? 'A' : 'a',
  1364.                    mc->deleted  ? 'D' : 'd',
  1365.                    mc->seen     ? 'S' : 's') == EOF) {
  1366.             /* BUG .. Need some error check here */
  1367.         }
  1368.     }
  1369.     fflush(LOCAL->index_stream);
  1370.  
  1371.     carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
  1372.     mm_nocritical(stream);
  1373.     mm_log("Check completed", NIL);
  1374.      return(1);
  1375. }
  1376.     
  1377.  
  1378.  
  1379.  
  1380.  
  1381. /*----------------------------------------------------------------------
  1382.   Carmel2 mail expunge mailbox
  1383.  
  1384. Args: stream  -- mail stream to expunge
  1385.  
  1386.  ----*/
  1387.  
  1388. void carmel2_expunge (stream)
  1389.         MAILSTREAM *stream;
  1390. {
  1391.     char        *index_file;
  1392.     long         msgno, new_msgno;
  1393.     FILE         *new_index;
  1394.     MESSAGECACHE *mc, *new_mc;
  1395.     ENVELOPE     *envelope;
  1396.     int           save_e;
  1397.     struct stat   sb;
  1398.     
  1399.     if (stream->readonly) {
  1400.         if(!stream->silent)
  1401.           mm_log ("Expunge ignored on readonly mailbox",NIL);
  1402.         return;
  1403.     }
  1404.     
  1405.     mm_critical(stream);
  1406.     carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK);
  1407.  
  1408.     strcpy(carmel_path_buf,
  1409.            (*LOCAL->calc_paths)(CalcPathCarmel2Expunge, stream->mailbox, 0));
  1410.  
  1411.     new_index = fopen(carmel_path_buf, "w");
  1412.     if(new_index == NULL) {
  1413.         goto fail;
  1414.     }
  1415.     if(fprintf(new_index, carmel2_s_o_f) == EOF)
  1416.       goto fail;
  1417.  
  1418.     new_msgno = 1;
  1419.     for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
  1420.         mc = MC(msgno);
  1421.         if(mc->deleted) {
  1422.             mm_expunged(stream, new_msgno);
  1423.             continue;
  1424.         }
  1425.  
  1426.         if(msgno != new_msgno) {
  1427.             new_mc        = MC(new_msgno);
  1428.             *new_mc       = *mc;
  1429.             new_mc->msgno = new_msgno;
  1430.             mc            = new_mc;
  1431.         }
  1432.  
  1433.         envelope = carmel2_fetchstructure(stream, msgno, NULL);
  1434.         if(envelope == NULL)
  1435.           goto fail;
  1436.  
  1437.         /* get this after envelope to offset is still valid in old file */
  1438.         mc->data1 = ftell(new_index); 
  1439.  
  1440.         if(carmel2_write_index(envelope, mc, new_index) < 0)
  1441.           goto fail;
  1442.         new_msgno++;
  1443.     }
  1444.  
  1445.     index_file = (*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0);
  1446.  
  1447.     /*--- Close it to make sure bits are really on disk across NFS. ---*/
  1448.     if(fclose(new_index) != EOF) {
  1449.         /*--- copy of index was a success, rename it ---*/
  1450.         unlink(index_file);
  1451.         link(carmel_path_buf, index_file); 
  1452.  
  1453.         /*--- Save the mail index size ----*/
  1454.         stat(index_file, &sb);
  1455.         LOCAL->index_size = sb.st_size;
  1456.  
  1457.         stream->nmsgs = new_msgno - 1;
  1458.         mm_exists(stream, stream->nmsgs);
  1459.         
  1460.         fclose(LOCAL->index_stream);
  1461.         LOCAL->index_stream = fopen(index_file, "r+");
  1462.     }
  1463.     unlink(carmel_path_buf); 
  1464.     carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
  1465.     mm_nocritical(stream);
  1466.     return;
  1467.  
  1468.   fail:
  1469.     save_e = errno;
  1470.     if(new_index != NULL)
  1471.       fclose(new_index);
  1472.     unlink(carmel_path_buf); 
  1473.     sprintf(carmel_error_buf, "Expunge failed: %s", strerror(save_e));
  1474.     mm_log(carmel_error_buf, WARN);
  1475.     carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
  1476.     mm_nocritical(stream);
  1477.     return;
  1478. }
  1479.  
  1480.  
  1481.  
  1482. /*----------------------------------------------------------------------
  1483.    Carmel2 mail copy message(s)
  1484.  
  1485.  Args: stream    - mail stream
  1486.        sequence  - message sequence
  1487.        mailbox   - destination mailbox, FQN
  1488.  
  1489.  Returns: T if copy successful, else NIL
  1490.   ----*/
  1491. long 
  1492. carmel2_copy(stream, sequence, mailbox)
  1493.         MAILSTREAM *stream;
  1494.         char       *sequence;
  1495.         char       *mailbox;
  1496. {
  1497.     ENVELOPE     *e;
  1498.     MESSAGECACHE *mc, new_mc;
  1499.     long          msgno, file_no, file_pos;
  1500.     int           fail;
  1501.     char         *file_name, *line;
  1502.     FILE         *dest_stream;
  1503.     struct stat   sb;
  1504.  
  1505.     if (!mail_sequence (stream,sequence)) /* get sequence to do */
  1506.       return(NIL);
  1507.  
  1508.     /*--- Get the file open (creating if needed) first ----*/
  1509.     file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Index, mailbox, 0, 0);
  1510.     if(file_name == NULL) 
  1511.       return(NIL);
  1512.  
  1513.     if(stat(file_name, &sb) < 0) {
  1514.         mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
  1515.         return(NIL);
  1516.     }
  1517.  
  1518.     dest_stream = fopen(file_name, "a+");
  1519.     if(dest_stream == NULL) {
  1520.         sprintf(carmel_error_buf, "Can't copy message to %s: %s",
  1521.                 mailbox, strerror(errno));
  1522.         mm_log(carmel_error_buf, ERROR);
  1523.         return(NIL);
  1524.     }
  1525.  
  1526.     mm_critical(stream);
  1527.  
  1528.     if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0) {
  1529.         mm_nocritical(stream);
  1530.         sprintf(carmel_error_buf,
  1531.                 "Mailbox %s locked, can't copy messages to it",
  1532.                 mailbox);
  1533.         mm_log(carmel_error_buf, ERROR);
  1534.         fclose(dest_stream);
  1535.         return(NIL);
  1536.     }
  1537.  
  1538.  
  1539.     /*--- Get the destination Carmel index open ----*/
  1540.     file_pos = ftell(dest_stream);
  1541.     fail = 0;
  1542.  
  1543.  
  1544.     for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
  1545.         mc = MC(msgno);
  1546.         if(!mc->sequence)
  1547.           continue;
  1548.  
  1549.         new_mc = *mc;
  1550.  
  1551.         if(LOCAL->new_file_on_copy) {
  1552.             new_mc.data2 = carmel2_copy_msg_file(stream, mc->data2, mailbox);
  1553.             if((long)new_mc.data2 < 0) {
  1554.                 fail = 1;
  1555.                 break;
  1556.             }
  1557.         }
  1558.  
  1559.         e = carmel2_fetchstructure(stream, msgno, NULL);
  1560.         if(carmel2_write_index(e, &new_mc, dest_stream) < 0) {
  1561.             fail = 1;
  1562.             break;
  1563.         }
  1564.  
  1565.         if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
  1566.             if((*LOCAL->aux_copy)(stream->local, mailbox, e, &new_mc)) {
  1567.                 fail = 1;
  1568.                 break;
  1569.             }
  1570.         }
  1571.     }
  1572.  
  1573.     if(fail) {
  1574.         ftruncate(fileno(dest_stream), file_pos);
  1575.     }
  1576.  
  1577.     fclose(dest_stream);
  1578.  
  1579.     carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
  1580.  
  1581.     mm_nocritical(stream);
  1582.  
  1583.     return(fail ? NIL : T);
  1584. }
  1585.  
  1586.  
  1587.  
  1588. /*----------------------------------------------------------------------
  1589.    Carmel2 mail move message(s)
  1590.  
  1591.  
  1592.   Returns: T if move successful, else NIL
  1593.  ----*/
  1594.  
  1595. long
  1596. carmel2_move (stream,sequence,mailbox)
  1597.         MAILSTREAM *stream;
  1598.         char *sequence;
  1599.         char *mailbox;
  1600. {
  1601.   long          i;
  1602.   MESSAGECACHE *elt;
  1603.  
  1604.   if (!(mail_sequence (stream,sequence) &&
  1605.         carmel2_copy (stream,sequence,mailbox))) return NIL;
  1606.                                 /* delete all requested messages */
  1607.   for (i = 1; i <= stream->nmsgs; i++)
  1608.     elt = MC(i);
  1609.     if (elt->sequence) {
  1610.       elt->deleted = T;         /* mark message deleted */
  1611.       LOCAL->dirty = T;         /* mark mailbox as dirty */
  1612.     }
  1613.   return T;
  1614. }
  1615.  
  1616.  
  1617.  
  1618. /*----------------------------------------------------------------------
  1619.  * Carmel2 garbage collect stream
  1620.  * Accepts: Mail stream
  1621.  *          garbage collection flags
  1622.  */
  1623.  
  1624. void carmel2_gc (stream, gcflags)
  1625.         MAILSTREAM *stream;
  1626.         long gcflags;
  1627. {
  1628.     /* No garbage collection in Carmel2 driver, not much to collect */
  1629. }
  1630.  
  1631.  
  1632.  
  1633. /*----------------------------------------------------------------------
  1634.     Handle MessageCache for carmel2 mail driver
  1635.  
  1636. Args: stream --
  1637.       msgno -- message number
  1638.       op    -- operation code
  1639.  
  1640. The carmel2 format keeps MESSAGECACHE entries in core for all messages
  1641. in the open mail folder so there isn't any cache flushing and rereading
  1642. that has to go on.
  1643.   ----*/
  1644. void *
  1645. carmel2_cache(stream, msgno, op)
  1646.         MAILSTREAM *stream;
  1647.         long msgno;
  1648.         long op;
  1649. {
  1650.   /* It's a carmel driver if first 6 letters of name are carmel */
  1651.   if(stream->dtb == NULL)
  1652.     return(0);
  1653.  
  1654.   if(struncmp2(stream->dtb->name, "carmel", 6) == 0) { 
  1655.     if(op == CH_MAKEELT)
  1656.       return(MC(msgno));
  1657.     else
  1658.       return(0);
  1659.   }
  1660.  
  1661.   /* Not a carmel2 or carmel driver, call the standard function. This works
  1662.      as long as there is only one other driver since we know it must be
  1663.      mm_cache().
  1664.    */
  1665.   return(mm_cache(stream, msgno, op));
  1666. }
  1667.         
  1668.  
  1669.  
  1670. /*----------------------------------------------------------------------
  1671.     Append a message to a mailbox
  1672.  
  1673. Args: mailbox -- The message to append the mail folder too
  1674.       message -- Message header and text in rfc822 \r\n format to append
  1675.  
  1676. Returns: T if all went OK, NIL if not
  1677.  ----*/
  1678. long
  1679. carmel2_append(stream, mailbox, flags, date, message)
  1680.      MAILSTREAM *stream;
  1681.      char       *mailbox, *flags, *date;
  1682.      STRING     *message;
  1683. {
  1684.     CARMEL2LOCAL local;
  1685.  
  1686.     /*---- A fake local data structure to pass to other functions---*/
  1687.     local.calc_paths = carmel2_calc_paths;
  1688.     local.carmel     = 0;
  1689.     local.aux_copy   = NULL;
  1690.  
  1691.     return(carmel2_append2(stream, &local, mailbox, flags, date, message));
  1692. }
  1693.       
  1694.  
  1695.  
  1696.  
  1697. /*----------------------------------------------------------------------
  1698.     Fetch the body structure for a camel message
  1699.  
  1700. Args: stream -- MAILSTREAM
  1701.       mc     -- The incore message cache entry for the message
  1702.  
  1703. Returns: body structure
  1704.  
  1705. Note, the envelope that results from the parse here is ignored. Only
  1706. the envelope from the index is actually used in the Carmel2 format.
  1707.  ----*/
  1708. static BODY *
  1709. carmel2_bodystruct(stream, mc)
  1710.      MESSAGECACHE *mc;
  1711.      MAILSTREAM   *stream;
  1712. {
  1713.     char     *header, *text, *file, *p;
  1714.     ENVELOPE *e_place_holder;
  1715.     BODY     *b;
  1716.     STRING    string_struct;
  1717.  
  1718.     header = carmel2_readmsg(stream, 1,  mc->rfc822_size, mc->data2);
  1719.     if(header == NULL)
  1720.       return(NULL);
  1721.  
  1722.     text = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
  1723.     if(text == NULL)
  1724.       return(NULL);
  1725.  
  1726.     INIT(&string_struct, mail_string, (void *)text, strlen(text));
  1727.     rfc822_parse_msg(&e_place_holder, &b, header, strlen(header),
  1728.                      &string_struct, mylocalhost(), carmel_20k_buf);
  1729.  
  1730.     mail_free_envelope(&e_place_holder);
  1731.  
  1732. #ifdef BWC 
  1733.     /* If there's no content type in the header and it's the carmel
  1734.        driver at the BWC set the type X-BWC-Glyph
  1735.      */
  1736.     for(p = header; *p; p++) 
  1737.       if(*p=='\n' && (*(p+1)=='C' || *(p+1)=='c') &&
  1738.         struncmp2(p+1,"content-type:", 13) == 0)
  1739.           break;
  1740.  
  1741.     if(!*p && LOCAL->carmel &&    /* Carmel, not Carmel2 */
  1742.        b->type == TYPETEXT &&     /* Type text */
  1743.        (b->subtype == NULL || strcmp(b->subtype,"PLAIN") == 0) && 
  1744.        ((int)mc->year) + 1969 < 1994) {
  1745.         /* Most mail in a pod mail store is in the BWC-Glyph character
  1746.            set, but there is no tag in the data on disk, so we fake it here.
  1747.            We expect after 1994 all mail generated in BWC-Glyph format
  1748.            will have proper tags!
  1749.          */
  1750.         b->subtype = cpystr("X-BWC-Glyph");
  1751.     }
  1752. #endif 
  1753.  
  1754.     return(b);
  1755. }
  1756.  
  1757.  
  1758.  
  1759. /*----------------------------------------------------------------------
  1760.     Parse an address in a Carmel2 format mail index
  1761.  
  1762. Args: line  -- The line from the index to parse
  1763.       addr  -- The address list to add to (if there is one)
  1764.  
  1765. Returns: address list
  1766.  ----*/
  1767. static ADDRESS *
  1768. carmel2_parse_address(line, addr, localhost)
  1769.      char    *line, *localhost;
  1770.      ADDRESS *addr;
  1771. {
  1772.     ADDRESS *a;
  1773.  
  1774.     if(addr == NULL) {
  1775.         addr = mail_newaddr();
  1776.         a = addr;
  1777.     } else {
  1778.         for(a = addr; a!= NULL && a->next != NULL; a = a->next);
  1779.         a->next = mail_newaddr();
  1780.         a = a->next;
  1781.     }
  1782.  
  1783.     line++;  /* Skip the tag chacater */
  1784.     a->personal = carmel2_parse_addr_field(&line);
  1785.     a->mailbox  = carmel2_parse_addr_field(&line);
  1786.     a->host     = carmel2_parse_addr_field(&line);
  1787.     a->adl      = carmel2_parse_addr_field(&line);
  1788. /*    if(a->host == NULL)
  1789.       a->host = cpystr(localhost); */ /* host can't be NULL */
  1790.       /* Yes it can for Internet group syntax */
  1791.     return(addr);
  1792. }
  1793.  
  1794.  
  1795.  
  1796. /*----------------------------------------------------------------------
  1797.    Parse the next address field out of a carmel address index entry
  1798.  
  1799. Args: string -- pointer to pointer to string
  1800.  
  1801. Returns: field in malloced string or NULL
  1802.  
  1803. Input string is a bunch of substrings separated by ^A. This function scans 
  1804. for the next ^A or end of string, cuts it out and returns it. The original
  1805. strings passed in is mangled
  1806. ----*/
  1807. static char *
  1808. carmel2_parse_addr_field(string)
  1809.      char **string;
  1810. {
  1811.     char *p, end, *start;
  1812.  
  1813.     start = p  = *string;
  1814.     while(*p > '\001')  /* Find end of sub string or string */
  1815.       p++;
  1816.     if((p - *string) == 0) {
  1817.         if(*p) p++;
  1818.         *string = p;
  1819.         return(NULL); /* If nothing found return nothing */
  1820.     }
  1821.     end = *p;       /* Save terminating character (^A or \0) */
  1822.     *p = '\0';     
  1823.     if(end)         /* If not end of string, get ready for next call */
  1824.       p++;
  1825.     *string = p;    /* Return pointer to next substring */
  1826.     return(cpystr(start));
  1827. }
  1828.  
  1829.  
  1830.     
  1831.     
  1832. /*----------------------------------------------------------------------
  1833.      Write an entry into a carmel2 index
  1834.  
  1835. Args: e      -- Envelope
  1836.       mc     -- Message Cache element
  1837.       stream -- File stream to write to
  1838.  
  1839. Returns: 0 if OK, -1 if failed
  1840. ----*/
  1841. carmel2_write_index(e, mc, stream)
  1842.      ENVELOPE     *e;
  1843.      MESSAGECACHE *mc;
  1844.      FILE         *stream;
  1845. {
  1846.     long f_start, f_end;
  1847.  
  1848.     f_start = ftell(stream);
  1849.  
  1850.     if(fprintf(stream, "%s\007\001xxxxxxxxxx\n", carmel2_s_o_m) == EOF)
  1851.       goto blah;
  1852.     if(fprintf(stream, "U%c%c%c%c%c____________________________\n",
  1853.             mc->flagged  ? 'F' : 'f',
  1854.             mc->recent   ? 'R' : 'r',
  1855.             mc->answered ? 'A' : 'a',
  1856.             mc->deleted  ? 'D' : 'd',
  1857.             mc->seen     ? 'S' : 's') == EOF)
  1858.       goto blah;
  1859.     if(fprintf(stream, "Z%d\n", mc->rfc822_size) == EOF)
  1860.       goto blah;
  1861.     if(fprintf(stream, "D%d\001%d\001%d\001%d\001%d\001%d\001%d\001%d\n",
  1862.             mc->year+1969, mc->month, mc->day, mc->hours, mc->minutes,
  1863.             mc->seconds, mc->zhours * (mc->zoccident ? 1 : -1),
  1864.                mc->zminutes) == EOF)
  1865.        goto blah;
  1866.     if(fprintf(stream, "Svmail\n") == EOF)
  1867.        goto blah;
  1868.     if(fprintf(stream, "P%d\n",mc->data2) == EOF)
  1869.        goto blah;
  1870.     if(carmel2_index_address(e->to,   'T', stream) < 0)
  1871.        goto blah;
  1872.     if(carmel2_index_address(e->from, 'F', stream) < 0)
  1873.        goto blah;
  1874.     if(carmel2_index_address(e->cc,   'C', stream) < 0)
  1875.        goto blah;
  1876.     if(carmel2_index_address(e->bcc,  'B', stream) < 0)
  1877.        goto blah;
  1878. #ifdef HAVE_RESENT
  1879.     if(carmel2_index_address(e->resent_to,   't', stream) < 0)
  1880.        goto blah;
  1881.     if(carmel2_index_address(e->resent_from, 'f', stream) < 0)
  1882.        goto blah;
  1883.     if(carmel2_index_address(e->resent_cc,   'c', stream) < 0)
  1884.        goto blah;
  1885.     if(carmel2_index_address(e->resent_bcc,  'b', stream) < 0)
  1886.        goto blah;
  1887. #endif
  1888.     if(carmel2_index_address(e->return_path, 'H', stream) < 0)
  1889.        goto blah;
  1890.     if(carmel2_index_address(e->sender,      'E', stream) < 0)
  1891.        goto blah;
  1892.     if(carmel2_index_address(e->reply_to,    'R', stream) < 0)
  1893.        goto blah;
  1894.     if(e->in_reply_to != NULL)
  1895.       if(fprintf(stream, "L%s\n", e->in_reply_to) == EOF)
  1896.        goto blah;
  1897.     if(e->remail != NULL)
  1898.       if(fprintf(stream, "r%s\n", e->remail) == EOF)
  1899.        goto blah;
  1900.     if(e->message_id != NULL)
  1901.       if(fprintf(stream, "I%s\n", e->message_id) == EOF)
  1902.        goto blah;
  1903.     if(e->newsgroups != NULL)
  1904.       if(fprintf(stream, "N%s\n", e->newsgroups) == EOF)
  1905.        goto blah;
  1906.     if(e->subject != NULL)
  1907.       if(fprintf(stream, "J%s\n", e->subject) == EOF)
  1908.         goto blah;
  1909.  
  1910.     /*--- figure out and write the offset ---*/
  1911.     f_end = ftell(stream);
  1912.     if(fseek(stream, f_start, 0) < 0)
  1913.       goto blah;
  1914.     if(fprintf(stream, "%s\007\001%10d\n", carmel2_s_o_m, f_end - f_start)==EOF)
  1915.       goto blah;
  1916.     if(fseek(stream, f_end, 0) < 0)
  1917.       goto blah;
  1918.  
  1919.     return(0);
  1920.  
  1921.   blah:
  1922.     /* Index entry is a bit of a mess now. Maybe we should try to truncate */
  1923.     return(-1);
  1924. }
  1925.  
  1926.  
  1927.  
  1928. /*----------------------------------------------------------------------
  1929.     Write an address entry into a carmel2 index
  1930.  
  1931. Args: addr   -- addresslist
  1932.       field  -- Field character specifier
  1933.       stream -- File stream to write to
  1934.  
  1935. Returns 0 if OK, -1 on error writing
  1936.  ---*/ 
  1937. static
  1938. carmel2_index_address(addr, field, stream)
  1939.     ADDRESS *addr;
  1940.     int      field;
  1941.     FILE    *stream;
  1942. {
  1943.     ADDRESS *a;
  1944.  
  1945.     for(a = addr; a != NULL; a = a->next) {
  1946.         if(fprintf(stream, "%c%s\001%s\001%s\001%s\n",
  1947.                    field,
  1948.                    a->personal == NULL ? "" : a->personal,
  1949.                    a->mailbox  == NULL ? "" : a->mailbox,
  1950.                    a->host     == NULL ? "" : a->host,
  1951.                    a->adl      == NULL ? "" : a->adl) == EOF)
  1952.           return(-1);
  1953.     }
  1954.     return(0);
  1955. }
  1956.     
  1957.  
  1958.  
  1959. /*----------------------------------------------------------------------
  1960.    Real work of reading mail data files
  1961.  
  1962. Args: stream 
  1963.       header_only -- return header if set, text if not
  1964.       file_size   -- The file size if known (saves a stat)
  1965.       file        -- name of the file to read
  1966.  
  1967. Returns buffer with text stored in internel buffer. The Carmel2 format
  1968. buffers the text of the current message and header in an internal
  1969. buffer. The buffer never shrinks and is expanded as needed, up to a
  1970. maximum. The text in the buffer is in CRLF format and is read in line
  1971. by line using stdio. It is believed this will be efficient on whatever
  1972. machine it is running on and will not use too much memory.  (There's
  1973. some extra memory used for buffering in stdio.) If a request is made
  1974. first for only the header, then only the header will be read in.  This
  1975. is a big efficiency win when the file is large and only the header is
  1976. needed. (In the Carmel2 format the header is genera lly not used, and
  1977. when it is it is with the body to do a MIME parse, but the pod format
  1978. does read the headers in to create the Carmel2 index.) An estimate is
  1979. made of the size needed to expand the file to convert the line ends
  1980. from LF to CRLF. The estimate alloca tes 10% extra space. If it
  1981. doesn't fit, the whole buffer will be expanded by 50% and the whole
  1982. read done over. When the header is read in a 30K buffer is allocated
  1983. initially (if the buffer is smaller than that initially). The 50%
  1984. increase is applied to the buffer when reading only the header.
  1985.   ----*/
  1986.  
  1987. char *
  1988. carmel2_readmsg(stream, header_only, file_size, file_num)
  1989.      MAILSTREAM *stream;
  1990.      int         header_only;
  1991.      int         file_num;
  1992.      long        file_size;
  1993. {
  1994.     FILE       *msg_stream;
  1995.     char       *p, *p_end, *file_name;
  1996.     int         past_header, not_eof;
  1997.     long        max_read;
  1998.     struct stat st;
  1999. #define DEBUGDEBUG 1
  2000. #ifdef DEBUGDEBUG
  2001.     char        debug_buf[500];
  2002. #endif
  2003.  
  2004.     file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
  2005.                                      stream->mailbox, file_num);
  2006.     if(file_name == NULL)
  2007.       return(NULL); /* Just in case; should never be invalid if open */
  2008. #ifdef DEBUGDEBUG
  2009.     sprintf(debug_buf, "READ RQ:\"%s\" HV:\"%s\"  RQ_HD:%d  HV_TXT:%d\n",
  2010.             file_name,
  2011.             LOCAL->buffered_file == NULL ? "" : LOCAL->buffered_file,
  2012.             header_only, LOCAL->buffered_header_and_text);
  2013.     mm_log(debug_buf, NIL);
  2014. #endif
  2015.  
  2016.     /*---- Check out what we have read already -----*/
  2017.     if(LOCAL->buffered_file != NULL &&
  2018.        strcmp(LOCAL->buffered_file, file_name) == 0) {
  2019.         /* The file is the same. Now have we read in the part
  2020.            that is wanted? If so return it.
  2021.          */
  2022.         if(header_only || LOCAL->buffered_header_and_text) {
  2023.             if(header_only) {
  2024. #ifdef DEBUGDEBUG
  2025.                 mm_log("....Returning buffered header\n", NIL);
  2026. #endif
  2027.                 return(LOCAL->msg_buf);
  2028.             } else {
  2029. #ifdef DEBUGDEBUG
  2030.                 mm_log("....Returning buffered text\n", NIL);
  2031. #endif
  2032.                 return(LOCAL->msg_buf_text_start);
  2033.             }
  2034.         }
  2035.     } else {
  2036.         /*-- Different file than last time, reset a few things --*/
  2037.         LOCAL->buffered_header_and_text = 0;
  2038.         LOCAL->msg_buf_text_offset      = 0L;
  2039.         if(LOCAL->buffered_file != NULL)
  2040.           fs_give((void **)&(LOCAL->buffered_file));
  2041.     }
  2042.  
  2043. #ifdef DEBUGDEBUG
  2044.      mm_log("....Reading file\n", NIL);
  2045. #endif
  2046.  
  2047.     /*----- Open the file ------*/
  2048.     /* Would actually be more efficient not to use stdio here */
  2049.     msg_stream = fopen(file_name, "r");
  2050.     if(msg_stream == NULL) {
  2051.         strcat(file_name, ".wid");
  2052.         msg_stream = fopen(file_name, "r");
  2053.         if(msg_stream == NULL)
  2054.            return(NULL);
  2055.     }
  2056.  
  2057.     /*---- Check the size of the file ----*/
  2058.     if(file_size == 0 && stat(file_name, &st) >= 0)
  2059.       file_size = st.st_size;
  2060.  
  2061.  
  2062.     /* ------Pick an amount to read -------
  2063.        Assume the header is less than MAX_HEADER. We don't want to
  2064.        allocate buffer for the whole message if we are just getting
  2065.        the header. 
  2066.      */
  2067.     max_read    = (file_size * 11) / 10;
  2068.     past_header = 0;
  2069.     p           = LOCAL->msg_buf;
  2070.     if(header_only) {
  2071.         max_read = min(max_read, CARMEL_MAX_HEADER);
  2072.     } else if(LOCAL->msg_buf_text_offset > 0) {
  2073.         past_header = 1;
  2074.         p = LOCAL->msg_buf_text_start;
  2075.         fseek(msg_stream, LOCAL->msg_buf_text_offset, 0);
  2076.     }
  2077.     if(max_read > CARMEL_MAXMESSAGESIZE)
  2078.       max_read = CARMEL_MAXMESSAGESIZE;
  2079.     if(max_read == 0)
  2080.       max_read = 1;  /* Forces buffer allocation below */
  2081.  
  2082.  
  2083.     /*----- Loop (re)reading the whole file 'till it fits ---*/
  2084.     /* This will fit the first time for all but the 
  2085.        strangest cases.
  2086.      */
  2087.     do {
  2088.         /*---- Make sure buffer is the right size ----*/              
  2089.         if(LOCAL->msg_buf_size < max_read) {
  2090.             /* Buffer not big, enough start whole read over */
  2091.             if(LOCAL->msg_buf != NULL)
  2092.               fs_give((void **)&(LOCAL->msg_buf));
  2093.             LOCAL->msg_buf      = fs_get(max_read);
  2094.             LOCAL->msg_buf_size = max_read;
  2095.             fseek(msg_stream, 0, 0);
  2096.             past_header = 0;
  2097.             p = LOCAL->msg_buf;
  2098.         }
  2099.     
  2100.         p_end = LOCAL->msg_buf + LOCAL->msg_buf_size - 3;
  2101.  
  2102.         while(p < p_end && (not_eof =(fgets(p, p_end-p, msg_stream) != NULL))){
  2103.             if(*p == '\n' && !past_header) {
  2104.                 *p++ = '\r';
  2105.                 *p++ = '\n';
  2106.                 *p++ = '\0';
  2107.                 past_header = 1;
  2108.                 LOCAL->msg_buf_text_offset = ftell(msg_stream);
  2109.                 LOCAL->msg_buf_text_start  = p;
  2110.                 if(header_only)
  2111.                   goto done;
  2112.                 else
  2113.                   continue;
  2114.             }
  2115.             p += strlen(p) - 1;    
  2116.             *p++ = '\r';
  2117.             *p++ = '\n';  
  2118.         }
  2119.         *p = '\0';
  2120.         if(!not_eof)
  2121.           goto done;
  2122.  
  2123.         /* If we're here, the buffer wasn't big enough, which
  2124.            is due to a message with most lines less than 10 characters 
  2125.            (the 10% addition for turning LF to CRLF wasn't enough).
  2126.            Increase it by 50% and try again, till we hit
  2127.            the largest message we can read 
  2128.           */
  2129.         max_read = min(CARMEL_MAXMESSAGESIZE, (max_read * 15) / 10);
  2130.         fseek(msg_stream, 0, 0);
  2131.     } while (1);
  2132.   done:
  2133.     if(p == p_end) 
  2134.       mm_log("Message truncated. It's is too large", WARN);
  2135.  
  2136.     fclose(msg_stream);
  2137.  
  2138.     /*---- Save the name of the file buffered file ----*/
  2139.     LOCAL->buffered_file             = cpystr(file_name);
  2140.     LOCAL->buffered_header_and_text |= !header_only;
  2141.  
  2142.     return(header_only ? LOCAL->msg_buf : LOCAL->msg_buf_text_start);
  2143. }
  2144.  
  2145.  
  2146. /*----------------------------------------------------------------------
  2147.     Parse basic/quick entries in a Carmel mailbox
  2148.  
  2149. Args: stream -- stream for mailbox
  2150.       file_pos -- File position in the index to start parsing at
  2151.  
  2152. Returns:  0 if parse succeeds
  2153.          -1 if it fails
  2154.  ----*/
  2155. static int
  2156. carmel2_parse_mail(stream, file_pos)
  2157.      MAILSTREAM *stream;
  2158.      long        file_pos;
  2159. {
  2160.     MESSAGECACHE *mc;
  2161.     long          nmsgs, next, last_line_read;
  2162.     int           found;
  2163.  
  2164.     nmsgs = stream->nmsgs;
  2165.  
  2166.     /*---- Get the start-of-message record ------*/
  2167.     fseek(LOCAL->index_stream, file_pos, 0);
  2168.     if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL)
  2169.        goto done_reading;
  2170.     if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0)
  2171.       goto bomb;
  2172.  
  2173.     while(1) {
  2174.         if(strlen(carmel_20k_buf) != carmel2_s_o_m_len + 13)
  2175.           goto bomb;
  2176.  
  2177.         nmsgs++;
  2178.         next      = atol(carmel_20k_buf+24);
  2179.         mc        = carmel2_new_mc(stream, nmsgs);
  2180.         mc->data1 = file_pos;
  2181.  
  2182.         /*-- Get the status line, It must be the first line in the entry ----*/
  2183.         if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
  2184.                  LOCAL->index_stream) == NULL)
  2185.           goto done_reading;
  2186.         if(*carmel_20k_buf != 'U' || strlen(carmel_20k_buf) != 35)
  2187.           goto bomb; /* Invalid format! */
  2188.         mc->flagged  = carmel_20k_buf[1] == 'F';
  2189.         mc->answered = carmel_20k_buf[3] == 'A';
  2190.         mc->deleted  = carmel_20k_buf[4] == 'D';
  2191.         mc->seen     = carmel_20k_buf[5] == 'S';
  2192.         mc->recent   = 0;
  2193.  
  2194.         /*---- Get the rest of the other interesting entries -----*/
  2195.         found = 0;
  2196.         while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
  2197.                     LOCAL->index_stream) != NULL &&
  2198.               found < 3 &&
  2199.               strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
  2200.           if (*carmel_20k_buf == 'Z') {
  2201.               mc->rfc822_size = atol(carmel_20k_buf+1);
  2202.               found++;
  2203.           } else if(*carmel_20k_buf == 'D') {
  2204.               carmel2_parse_date(mc, carmel_20k_buf+1);
  2205.               found++;
  2206.           } else if(*carmel_20k_buf == 'P') {
  2207.               mc->data2 = atoi(carmel_20k_buf+1);
  2208.               found++;
  2209.           }
  2210.  
  2211.         /*-------- Now find the next index entry ---------*/
  2212.         last_line_read = ftell(LOCAL->index_stream);
  2213.         file_pos += next;
  2214.         fseek(LOCAL->index_stream, file_pos, 0); /* try the offset first */
  2215.         if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
  2216.                  LOCAL->index_stream) == NULL) 
  2217.            break;
  2218.         if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0){
  2219.             /*-- Didn't match what was seeked to, back off and read lines --*/ 
  2220.             fseek(LOCAL->index_stream, last_line_read, 0);
  2221.             do {
  2222.                 file_pos = ftell(LOCAL->index_stream);
  2223.                 if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
  2224.                          LOCAL->index_stream) == NULL)
  2225.                   goto done_reading;
  2226.             }while(strncmp(carmel_20k_buf,carmel2_s_o_m,carmel2_s_o_m_len)!=0);
  2227.         }
  2228.     }
  2229.   done_reading:
  2230.     if(stream->nmsgs != nmsgs) {
  2231.         stream->nmsgs = nmsgs;
  2232.         mm_exists(stream, nmsgs);
  2233.     }
  2234.     return(0);
  2235.  
  2236.   bomb:
  2237.    return(-1);
  2238. }
  2239.  
  2240.  
  2241.  
  2242. /* This killer macro is from bezerk.h. It's only needed at sites that don't
  2243.  * escape "From " lines with ">From " unless absolutely necessary (The UW).
  2244.  */
  2245.  
  2246. /* Validate line known to start with ``F''
  2247.  * Accepts: pointer to candidate string to validate as a From header
  2248.  *          return pointer to end of date/time field
  2249.  *          return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
  2250.  *          return pointer to offset from t of time zone (if non-zero)
  2251.  * Returns: T if valid From string, t,ti,zn set; else NIL
  2252.  */
  2253.  
  2254. #define VALID(s,x,ti,zn) \
  2255.   (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && (s[4] == ' ') && \
  2256.   (x = strchr (s+5,'\n')) && \
  2257.   ((x-s < 41) || ((ti = ((x[-2] == ' ') ? -14 : (x[-3] == ' ') ? -15 : \
  2258.                          (x[-4] == ' ') ? -16 : (x[-5] == ' ') ? -17 : \
  2259.                          (x[-6] == ' ') ? -18 : (x[-7] == ' ') ? -19 : \
  2260.                          (x[-8] == ' ') ? -20 : (x[-9] == ' ') ? -21 : \
  2261.                          (x[-10]== ' ') ? -22 : (x[-11]== ' ') ? -23 : 0)) && \
  2262.                   (x[ti]   == ' ') && (x[ti+1] == 'r') && (x[ti+2] == 'e') && \
  2263.                   (x[ti+3] == 'm') && (x[ti+4] == 'o') && (x[ti+5] == 't') && \
  2264.                   (x[ti+6] == 'e') && (x[ti+7] == ' ') && (x[ti+8] == 'f') && \
  2265.                   (x[ti+9] == 'r') && (x[ti+10]== 'o') && (x[ti+11]== 'm') && \
  2266.                   (x += ti)) || T) && \
  2267.   (x-s >= 27) && \
  2268.   ((x[ti = -5] == ' ') ? ((x[-8] == ':') ? !(zn = 0) : \
  2269.                           ((x[ti = zn = -9] == ' ') || \
  2270.                            ((x[ti = zn = -11] == ' ') && \
  2271.                             ((x[-10] == '+') || (x[-10] == '-'))))) : \
  2272.    ((x[zn = -4] == ' ') ? (x[ti = -9] == ' ') : \
  2273.     ((x[zn = -6] == ' ') && ((x[-5] == '+') || (x[-5] == '-')) && \
  2274.      (x[ti = -11] == ' ')))) && \
  2275.   (x[ti - 3] == ':') && (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
  2276.   (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && (x[ti - 11] == ' ')
  2277.  
  2278.  
  2279. /* You are not expected to understand this macro, but read the next page if
  2280.  * you are not faint of heart.
  2281.  *
  2282.  * Known formats to the VALID macro are:
  2283.  *              From user Wed Dec  2 05:53 1992
  2284.  * BSD          From user Wed Dec  2 05:53:22 1992
  2285.  * SysV         From user Wed Dec  2 05:53 PST 1992
  2286.  * rn           From user Wed Dec  2 05:53:22 PST 1992
  2287.  *              From user Wed Dec  2 05:53 -0700 1992
  2288.  *              From user Wed Dec  2 05:53:22 -0700 1992
  2289.  *              From user Wed Dec  2 05:53 1992 PST
  2290.  *              From user Wed Dec  2 05:53:22 1992 PST
  2291.  *              From user Wed Dec  2 05:53 1992 -0700
  2292.  * Solaris      From user Wed Dec  2 05:53:22 1992 -0700
  2293.  *
  2294.  * Plus all of the above with `` remote from xxx'' after it. Thank you very
  2295.  * much, smail and Solaris, for making my life considerably more complicated.
  2296.  */
  2297.  
  2298. /*
  2299.  * What?  You want to understand the VALID macro anyway?  Alright, since you
  2300.  * insist.  Actually, it isn't really all that difficult, provided that you
  2301.  * take it step by step.
  2302.  *
  2303.  * Line 1       Validates that the 2-5th characters are ``rom ''.
  2304.  * Line 2       Sets x to point to the end of the line.
  2305.  * Lines 3-12   First checks to see if the line is at least 41 characters long.
  2306.  *              If so, it scans backwards up to 10 characters (the UUCP system
  2307.  *              name length limit due to old versions of UNIX) to find a space.
  2308.  *              If one is found, it backs up 12 characters from that point, and
  2309.  *              sees if the string from there is `` remote from''.  If so, it
  2310.  *              sets x to that position.  The ``|| T'' is there so the parse
  2311.  *              will still continue.
  2312.  * Line 13      Makes sure that there are at least 27 characters in the line.
  2313.  * Lines 14-17  Checks if the date/time ends with the year.  If so, It sees if
  2314.  *              there is a colon 3 characters further back; this would mean
  2315.  *              that there is no timezone field and zn is set to 0 and ti is
  2316.  *              left in front of the year.  If not, then it expects there to
  2317.  *              either to be a space four characters back for a three-letter
  2318.  *              timezone, or a space six characters back followed by a + or -
  2319.  *              for a numeric timezone.  If a timezone is found, both zn and
  2320.  *              ti are the offset of the space immediately before it.
  2321.  * Lines 18-20  Are the failure case for a date/time not ending with a year in
  2322.  *              line 14.  If there is a space four characters back, it is a
  2323.  *              three-letter timezone; there must be a space for the year nine
  2324.  *              characters back.  Otherwise, there must be a space six
  2325.  *              characters back and a + or - five characters back to indicate a
  2326.  *              numeric timezone and a space eleven characters back to indicate
  2327.  *              a year.  zn and ti are set appropriately.
  2328.  * Line 21      Make sure that the string before ti is of the form hh:mm or
  2329.  *              hh:mm:ss.  There must be a colon three characters back, and a
  2330.  *              space six or nine characters back (depending upon whether or
  2331.  *              not the character six characters back is a colon).  ti is set
  2332.  *              to be the offset of the space immediately before the time.
  2333.  * Line 22      Make sure the string before ti is of the form www mmm dd.
  2334.  *              There must be a space three characters back (in front of the
  2335.  *              day), one seven characters back (in front of the month), and
  2336.  *              one eleven characters back (in front of the day of week).
  2337.  *
  2338.  * Why a macro?  It gets invoked a *lot* in a tight loop.  On some of the
  2339.  * newer pipelined machines it is faster being open-coded than it would be if
  2340.  * subroutines are called.
  2341.  *
  2342.  * Why does it scan backwards from the end of the line, instead of doing the
  2343.  * much easier forward scan?  There is no deterministic way to parse the
  2344.  * ``user'' field, because it may contain unquoted spaces!  Yes, I tested it to
  2345.  * see if unquoted spaces were possible.  They are, and I've encountered enough
  2346.  * evil mail to be totally unwilling to trust that ``it will never happen''.
  2347.  */
  2348.  
  2349.  
  2350. /*----------------------------------------------------------------------
  2351.     Get the new mail out of the spooled mail file
  2352.   
  2353.  Args: stream  -- The inbox stream to add mail too
  2354.        spool   -- The path name of the spool file
  2355.        mailbox -- Name user sees for this, used only for error reporting
  2356.  
  2357.  Result:
  2358.  
  2359.  - Lock the spool mail file with bezerk_lock
  2360.  - Get the carmel2 index open and remember where we started in it
  2361.  - Make buffer big enough for biggest header we'll mess with
  2362.  - Loop reading all the message out of the spool file:
  2363.    - Get a new data file for the message and open it
  2364.    - Read the header of the message into the big buffer while...
  2365.     - writing the message into the new data file
  2366.    - finish writing the text of the message into the data file
  2367.    - If all went well bump the message count and say it exists
  2368.    - Parse the header of the message to get an envelope, date and size
  2369.    - Write an entry into the carmel2 index
  2370.  - Unlink and create (to zero) the spool file and unlock it
  2371.  
  2372. The carmel inbox should be locked when this is called and mm_critical
  2373. should be called around this function.
  2374.  ----*/
  2375. void
  2376. carmel2_spool_mail(stream, spool, mailbox, clear_spool_file) 
  2377.      MAILSTREAM *stream;
  2378.      char       *spool, *mailbox;
  2379.      int         clear_spool_file;
  2380. {
  2381.     char         *p, *eof, *from_p;
  2382.     int           n, size, fd, in_header, from_i1, from_i2;
  2383.     long          file_pos, index_file_pos, byte_count, start_of_append;
  2384.     FILE         *spool_stream, *data_stream;
  2385.     ENVELOPE     *envelope;
  2386.     BODY         *b;
  2387.     MESSAGECACHE *mc;
  2388.     STRING        string_struct;
  2389. #ifdef BWC
  2390.     int           is_wide;
  2391. #endif    
  2392.  
  2393.     /*--- Get the locks set and files open-----*/
  2394.     fd = carmel2_bezerk_lock(spool, mailbox);
  2395.     if(fd < 0) {
  2396.         return;
  2397.     }
  2398.     spool_stream = fdopen(fd, "r");
  2399.     fseek(LOCAL->index_stream, 0L, 2);
  2400.     start_of_append = ftell(LOCAL->index_stream);
  2401.  
  2402.     /*--- Make buffer big enough for largest allowable header ----*/
  2403.     if(LOCAL->msg_buf_size < CARMEL_MAX_HEADER) {
  2404.         if(LOCAL->msg_buf != NULL)
  2405.           fs_give((void **)&(LOCAL->msg_buf));
  2406.         LOCAL->msg_buf_size = CARMEL_MAX_HEADER;
  2407.         LOCAL->msg_buf = fs_get(CARMEL_MAX_HEADER);
  2408.     }
  2409.     LOCAL->buffered_header_and_text = 0;
  2410.     LOCAL->msg_buf_text_offset      = 0L;
  2411.     if(LOCAL->buffered_file != NULL)
  2412.       fs_give((void **)&(LOCAL->buffered_file));
  2413.  
  2414.  
  2415.     /*---- Read (and ignore) the first line with the "From " in it ----*/
  2416.     eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream); 
  2417.  
  2418.     /*----- Loop getting messages ----*/
  2419.     while(eof != NULL) {
  2420.  
  2421.         /*----- get a data file for the new message ----*/
  2422.         n = carmel2_new_data_file(stream->local, stream->mailbox);
  2423.         data_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Data,
  2424.                                                  stream->mailbox, n),"w");
  2425.         if(data_stream == NULL)
  2426.           goto backout;
  2427.         file_pos  = ftell(spool_stream);
  2428.         p         = LOCAL->msg_buf;
  2429.         in_header = 1;
  2430.         byte_count = 0L;
  2431. #ifdef BWC
  2432.         is_wide = 0;
  2433. #endif
  2434.         
  2435.  
  2436.         /*--------------------------------------------------------------------
  2437.             Read the message in line by line, writing it out to the
  2438.             new data file. Also acculamuate a copy of the header in
  2439.             a buffer for parsing
  2440.           ---*/
  2441.         eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
  2442.         while(eof != NULL){
  2443.             if(VALID(carmel_20k_buf, from_p, from_i1, from_i2))
  2444.               break;
  2445.     
  2446.             if(in_header) {
  2447. #ifdef BWC
  2448.                 is_wide |= carmel_match_glyph_wide(carmel_20k_buf);
  2449. #endif
  2450.                 if(*carmel_20k_buf == '\n') {
  2451.                     /* Encountered first blank line, end of header */
  2452.                     in_header = 0;
  2453.                     *p = '\0';
  2454.                 } else {
  2455.                     if(p - LOCAL->msg_buf + strlen(carmel_20k_buf) >
  2456.                        LOCAL->msg_buf_size){
  2457.                         /* out of room in buffer, end it */
  2458.                         in_header = 0;
  2459.                         *p = '\0';
  2460.                     } else {
  2461.                         strcpy(p, carmel_20k_buf);
  2462.                         p +=strlen(p);
  2463.                     }
  2464.                 }
  2465.             }
  2466.  
  2467.             /*----- Write the message into the file -----*/
  2468.             byte_count += strlen(carmel_20k_buf);
  2469.             if(carmel_20k_buf[0] == '>' && carmel_20k_buf[1] == 'F' &&
  2470.                carmel_20k_buf[2] == 'r' && carmel_20k_buf[3] == 'o' &&
  2471.                carmel_20k_buf[4] == 'm' && carmel_20k_buf[5] == ' ')  {
  2472.                 if(fputs(carmel_20k_buf + 1, data_stream) == EOF)
  2473.                   goto backout;
  2474.                 byte_count -= 1;
  2475.             } else {
  2476.                 if(fputs(carmel_20k_buf, data_stream) == EOF)
  2477.                   goto backout;
  2478.             }
  2479.             eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
  2480.         }
  2481.         fclose(data_stream);
  2482. #ifdef BWC
  2483.         if(is_wide) {
  2484.             sprintf(carmel_path_buf, "%s.wid",
  2485.                     (*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n)
  2486.                     );
  2487.             rename((*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n),
  2488.                    carmel_path_buf);
  2489.         }
  2490. #endif
  2491.  
  2492.         /*---- get a new MESSAGECACHE to store it in -----*/
  2493.         mc = carmel2_new_mc(stream, stream->nmsgs + 1);
  2494.  
  2495.         /*------ Parse the message header ------*/
  2496.         INIT(&string_struct, mail_string, (void *)"", 0);
  2497.         rfc822_parse_msg(&envelope, &b, LOCAL->msg_buf, strlen(LOCAL->msg_buf),
  2498.                          &string_struct, mylocalhost(), carmel_20k_buf);
  2499.         carmel2_parse_bezerk_status(mc, LOCAL->msg_buf);
  2500.         carmel2_rfc822_date(mc, LOCAL->msg_buf);
  2501.         mc->rfc822_size = byte_count; 
  2502.         mc->data2 = n;
  2503.         mc->data1 = ftell(LOCAL->index_stream);
  2504.         mc->recent = 1;
  2505.  
  2506.         /*----- Now add the message to the Carmel2 index ------*/
  2507.         if(carmel2_write_index(envelope, mc, LOCAL->index_stream) < 0)
  2508.           goto backout;
  2509.  
  2510.         /*----- Write message into auxiliary index (plain carmel) ----*/
  2511.         if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
  2512.             if((*LOCAL->aux_copy)(stream->local, mailbox, envelope, mc)) {
  2513.                 /* BUG - this error may leave things half done, but will
  2514.                    only result in duplicated mail */
  2515.                 goto backout; 
  2516.             }
  2517.         }
  2518.  
  2519.         /*---- All went well, let the user know -----*/
  2520.         stream->nmsgs++;
  2521.         mm_exists(stream, stream->nmsgs);
  2522.  
  2523.         mail_free_envelope(&envelope);
  2524.     }
  2525.  
  2526.     fflush(LOCAL->index_stream); /* Force new index entries onto disk */
  2527.     fclose(spool_stream);
  2528.     if(clear_spool_file) {
  2529.         unlink(spool);
  2530.         close(creat(spool, 0600));
  2531.     }
  2532.     carmel2_bezerk_unlock(fd, spool);
  2533.     return;
  2534.  
  2535.   backout:
  2536.     sprintf(carmel_error_buf, "Error incorporating new mail into \"%s\": %s",
  2537.             carmel_parse_mb_name(mailbox,'\0')->mailbox, strerror(errno));
  2538.     /* bug in above call to parse_mb -- should have version passed in */
  2539.     mm_log(carmel_error_buf, ERROR);
  2540.     fflush(LOCAL->index_stream);
  2541.     ftruncate(fileno(LOCAL->index_stream), start_of_append);
  2542.     carmel2_bezerk_unlock(fd, spool);
  2543. }
  2544.  
  2545.  
  2546.  
  2547. /*----------------------------------------------------------------------
  2548.    Copy the actual data file when copying a message
  2549.  
  2550. Returns: -1 for failure
  2551.             otherwise the number of the new file, 
  2552.  
  2553.   ----*/        
  2554. static  
  2555. carmel2_copy_msg_file(stream, orig_file_number, new_mailbox)
  2556.      MAILSTREAM *stream;
  2557.      int         orig_file_number;
  2558.      char       *new_mailbox;
  2559. {
  2560.     char *file_name;
  2561.     int   wide, e, new_file_num, n, orig_fd, new_fd;
  2562.  
  2563.     /*---- Open the orginal file ----*/
  2564.     wide = 0;
  2565.     file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
  2566.                                      new_mailbox,orig_file_number);
  2567.     if(file_name == NULL)
  2568.       return(-1);
  2569.  
  2570.     orig_fd = open(file_name, O_RDONLY);
  2571.     if(orig_fd < 0 && LOCAL->carmel) {
  2572.         strcat(file_name, ".wid");
  2573.         orig_fd = open(file_name, O_RDONLY);
  2574.         if(orig_fd < 0)
  2575.           goto bomb;
  2576.         wide = 1;
  2577.     } else {
  2578.         goto bomb;
  2579.     }
  2580.  
  2581.     /*----- Open and create the new file ------*/
  2582.     new_file_num = carmel2_new_data_file(stream->local, new_mailbox);
  2583.     if(new_file_num < 0)
  2584.       goto bomb;
  2585.     file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
  2586.                                      new_mailbox, new_file_num);
  2587.     if(wide)
  2588.       strcat(file_name, ".wid");
  2589.     new_fd = open(file_name, O_WRONLY | O_CREAT, 0600);
  2590.     if(new_fd < 0) {
  2591.         goto bomb;
  2592.     }
  2593.  
  2594.     /*---- Copy the bits ------*/
  2595.     e = 0;
  2596.     while((n = read(orig_fd, carmel_20k_buf, sizeof(carmel_20k_buf))) >0) {
  2597.         if(write(new_fd, carmel_20k_buf, n) < 0) {
  2598.             e = errno;
  2599.             break;
  2600.         }
  2601.     }
  2602.  
  2603.     /*---- Close the streams and handle any errors ----*/
  2604.     close(orig_fd);
  2605.     close(new_fd);
  2606.  
  2607.     if(e == 0)
  2608.       return(new_file_num);  /* All is OK */
  2609.     
  2610.     /*--- something didn't go right ---*/
  2611.   bomb:
  2612.     unlink(file_name);
  2613.     sprintf(carmel_error_buf, "Error copying message to %s: %s",
  2614.             new_mailbox, strerror(errno));
  2615.     mm_log(carmel_error_buf, ERROR);
  2616.     return(-1);
  2617. }
  2618.  
  2619.  
  2620.  
  2621.  
  2622.  
  2623.  
  2624. /*----------------------------------------------------------------------
  2625.        Locking for Carmel and Pod formats
  2626.  
  2627. Args: stream --  Mail stream we're operating on
  2628.       file   --  Mail file name
  2629.       write  --  Flag indicating we want write access
  2630.  
  2631. Retuns: -1 if lock fails, 0 if it succeeds
  2632.  
  2633. - There are two locks. Plain locks and write locks. They are created
  2634.   about the same way, but have different names. The time out on the write
  2635.   locks is much shorter, because it should never be held for very long.
  2636.  
  2637. - Hitching (links in file system) post locking is used so it will work
  2638.   across NFS. Flock() could be used as it has two advantages. First it
  2639.   is more efficient, second the locks will disolve automatically when the 
  2640.   process dies. The efficiency is not of great concern, and the
  2641.   process should not (theoretically) die unless it crashes due to a bug
  2642.   or it is abnormally terminated. The advantage of locking across NFS
  2643.   is considered greater than the advantages of flock().
  2644.  
  2645. - The mod time of the lock file is updated everytime mail_check()
  2646.   or mail_ping() is called and the mod time on the lock file is recorded.
  2647.   This is so it can be determined that the lock file is current.
  2648.  
  2649. - When a read lock file over 30 or a write lock over 5 minutes old is
  2650.   encountered it is assumed the lock is old and should be overridden
  2651.   (because the process crashed or was killed). 
  2652.  
  2653. - Everytime the mod time on the lock file is updated (on calls to
  2654.   mail_check() and mail_ping()), the mod time of the lock file is
  2655.   checked against the record of what it was last set to. If the mod times 
  2656.   don't match the lock has been broken and overridden. Then the original
  2657.   Pine should go into read-only mode.... This is only likely to happen if
  2658.   someone suspends (^Z's) the process for more than 30 minutes, and another
  2659.   process is started.
  2660.   ----*/
  2661. int
  2662. carmel2_lock(local, file, write)
  2663.      CARMEL2LOCAL *local;
  2664.      char         *file;
  2665.      int           write;
  2666. {
  2667.     char        *hitch, lock[CARMEL_PATHBUF_SIZE], error_mess[200], *l_path;
  2668.     struct stat sb;
  2669.     int         n, link_result, hitch_fd, timer_set, l;
  2670.     long        override, timer;
  2671.  
  2672.     /* Set the length of time for override. It is 5 minutes for a 
  2673.        write lock (no process should have it for that long) and
  2674.        30 minutes for a read lock, that's 30 minutes since the
  2675.        lock file was last touched. */
  2676.     override = 60 * (write ? 5 : 30);
  2677.     timer    = -1;
  2678.  
  2679.     /*----- Get the lock file and hitch names -----*/
  2680.     l_path = (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
  2681.                                               CalcPathCarmel2ReadLock,
  2682.                                       file, 0);
  2683.     if(l_path == NULL)
  2684.       return(-1);
  2685.     strcpy(lock, l_path);
  2686.     /* copy lock file into bufferl call it hitch, unique thing added below */
  2687.     hitch = carmel_path_buf;
  2688.     hitch = strcpy(hitch, lock);
  2689.     l = strlen(hitch);
  2690.     
  2691.     do {
  2692.         /*--- First create hitching post ----*/
  2693.         for(n = time(0) % 6400; ; n += 10007) {
  2694.             /* Get random file name, that's not too long */
  2695.             sprintf(hitch + l, "_%c%c", '0' + (n % 80) , '0' + (n/80));
  2696.             if(stat(hitch, &sb) < 0)
  2697.               break; /* Got a name that doesn't exist */
  2698.         }
  2699.         hitch_fd = open(hitch, O_CREAT, 0666);
  2700.         if(hitch_fd < 0) {
  2701.             sprintf(error_mess, "Error creating lock file \"%s\": %s",
  2702.                     hitch, strerror(errno));
  2703.             mm_log(error_mess, WARN);
  2704.             return(-1);
  2705.         }
  2706.         close(hitch_fd);
  2707.     
  2708.         /*----- Got a hitching post, now try link -----*/
  2709.         link_result = link(hitch, lock);
  2710.         stat(lock, &sb);
  2711.         unlink(hitch);
  2712.         if(link_result == 0 && sb.st_nlink == 2) {
  2713.             /*------ Got the lock! ------*/
  2714.             stat(lock, &sb);
  2715.             if(write)
  2716.               local->write_lock_mod_time = sb.st_mtime;
  2717.             else
  2718.               local->read_lock_mod_time = sb.st_mtime;
  2719.             return(0);
  2720.         }
  2721.  
  2722.         /*----- Check and override if lock is too old -----*/
  2723.         if(sb.st_mtime + override < time(0)) {
  2724.             unlink(lock); /* Lock is old, override it */
  2725.             timer = 100; /* Get us around the loop again */
  2726.             continue;
  2727.         } else {
  2728.             if(timer < 0) /* timer not set */
  2729.               timer = sb.st_mtime + override - time(0);
  2730.         }
  2731.  
  2732.         /*----- Will user wait till time for override? -----*/
  2733.         if(!write || timer > 5 * 60) {
  2734.             return(-1); /* Not more that 5 minutes */
  2735.         }
  2736.  
  2737.         /*----- Try again, and tell user we're trying -------*/
  2738.         if(!(timer % 15)) {
  2739.             sprintf(error_mess,
  2740.                     "Please wait. Mailbox %s is locked for %d more seconds",
  2741.                     file, timer);
  2742.             mm_log(error_mess, WARN);
  2743.         }
  2744.         timer--;
  2745.         sleep(1);
  2746.     } while(timer > 0);
  2747.  
  2748.     return(-1);
  2749. }
  2750.  
  2751.  
  2752.  
  2753. /*----------------------------------------------------------------------
  2754.    Unlock a carmel mail stream
  2755.  
  2756. Args: stream -- The mailstream that is locked
  2757.       mailbox -- FQN of mailbox to lock ( e.g. #carmel#foo )
  2758.       write   -- flag to set if it is a write lock
  2759.  
  2760. Nothing is returned
  2761.   ----*/
  2762. void
  2763. carmel2_unlock(local, mailbox, write)
  2764.      CARMEL2LOCAL *local;
  2765.      char         *mailbox;
  2766.      int           write;
  2767. {
  2768.     char        lock[CARMEL_PATHBUF_SIZE];
  2769.     struct stat sb;
  2770.  
  2771.     strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
  2772.                                               CalcPathCarmel2ReadLock,
  2773.                                       mailbox, 0));
  2774.  
  2775.     if(stat(lock, &sb) < 0)
  2776.       return; /* Hmmm... lock already broken */
  2777.  
  2778.     if(sb.st_mtime !=
  2779.        (write ? local->write_lock_mod_time : local->read_lock_mod_time))
  2780.       return; /* Hmmm... not our lock */
  2781.     
  2782.     unlink(lock);
  2783. }
  2784.  
  2785.     
  2786.     
  2787. /*----------------------------------------------------------------------
  2788.     Keep the mod date on the lock file fresh 
  2789.  
  2790. Args: stream --
  2791.       file   -- the name of the mailbox the lock is for
  2792.       write  -- set if this is a write lock
  2793.  
  2794. Returns: 0 if update was OK
  2795.         -1 if something bad happened, like the lock was stolen
  2796.  ----*/
  2797. static int 
  2798. carmel2_update_lock(local, file, write)
  2799.      CARMEL2LOCAL *local;
  2800.      char         *file;
  2801.      int           write;
  2802. {
  2803.     char        lock[CARMEL_PATHBUF_SIZE];
  2804.     struct      timeval tp[2];
  2805.     struct stat sb;
  2806.  
  2807.     strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
  2808.                                               CalcPathCarmel2ReadLock,
  2809.                                       file, 0));
  2810.  
  2811.     if(stat(lock, &sb) < 0) {
  2812.         /* Lock file stolen, oh oh */
  2813.         return(-1);
  2814.     }
  2815.  
  2816.     if(sb.st_mtime !=
  2817.        (write ? local->write_lock_mod_time : local->read_lock_mod_time)) {
  2818.         /* Not our lock anymore , oh oh */
  2819.         return(-1);
  2820.     }
  2821.  
  2822.     gettimeofday (&tp[0],NIL);  /* set atime to now */
  2823.     gettimeofday (&tp[1],NIL);  /* set mtime to now */
  2824.     utimes(lock, tp);
  2825.  
  2826.     if(write)
  2827.       local->write_lock_mod_time = tp[1].tv_sec;
  2828.     else
  2829.       local->read_lock_mod_time = tp[1].tv_sec;
  2830.     return(0);
  2831. }
  2832.  
  2833.  
  2834.  
  2835. /*----------------------------------------------------------------------
  2836.    Berkeley open and lock mailbox
  2837.  
  2838. This is mostly ripped off from the Bezerk driver
  2839.  ----*/
  2840.  
  2841. static int 
  2842. carmel2_bezerk_lock (spool, file)
  2843.         char *spool, *file;
  2844. {
  2845.   int fd,ld,j;
  2846.   int i = BEZERKLOCKTIMEOUT * 60 - 1;
  2847.   struct timeval tp;
  2848.   struct stat sb;
  2849.   char *hitch, *lock;
  2850.  
  2851.   lock = carmel_path_buf;
  2852.   sprintf(lock, "%s.lock", spool);
  2853.   do {                          /* until OK or out of tries */
  2854.     gettimeofday (&tp,NIL);     /* get the time now */
  2855. #ifdef NFSKLUDGE
  2856.   /* SUN-OS had an NFS, As kludgy as an albatross;
  2857.    * And everywhere that it was installed, It was a total loss.  -- MRC 9/25/91
  2858.    */
  2859.                                 /* build hitching post file name */
  2860.     hitch = carmel_20k_buf;
  2861.     sprintf(hitch, "%s.%d.%d.",carmel_path_buf,time (0),getpid ());
  2862.     j = strlen (hitch); /* append local host name */
  2863.     gethostname (hitch + j,(MAILTMPLEN - j) - 1);
  2864.                                 /* try to get hitching-post file */
  2865.     if ((ld = open (hitch, O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) {
  2866.                                 /* prot fail & non-ex, don't use lock files */
  2867.       if ((errno == EACCES) && (stat (hitch, &sb))) *lock = '\0';
  2868.       else {                    /* otherwise something strange is going on */
  2869.         sprintf (carmel_20k_buf,"Error creating %s: %s",lock,strerror (errno));
  2870.         mm_log (carmel_20k_buf, WARN);  /* this is probably not good */
  2871.                                 /* don't use lock files if not one of these */
  2872.         if ((errno != EEXIST) && (errno != EACCES)) *lock = '\0';
  2873.       }
  2874.     }
  2875.     else {                      /* got a hitching-post */
  2876.       chmod (hitch,0666);       /* make sure others can break the lock */
  2877.       close (ld);               /* close the hitching-post */
  2878.       link (hitch,lock);        /* tie hitching-post to lock, ignore failure */
  2879.       stat (hitch, &sb);        /* get its data */
  2880.       unlink (hitch);           /* flush hitching post */
  2881.       /* If link count .ne. 2, hitch failed.  Set ld to -1 as if open() failed
  2882.          so we try again.  If extant lock file and time now is .gt. file time
  2883.          plus timeout interval, flush the lock so can win next time around. */
  2884.       if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
  2885.           (tp.tv_sec > sb.st_ctime + BEZERKLOCKTIMEOUT * 60)) unlink (lock);
  2886.     }
  2887.  
  2888. #else
  2889.   /* This works on modern Unix systems which are not afflicted with NFS mail.
  2890.    * "Modern" means that O_EXCL works.  I think that NFS mail is a terrible
  2891.    * idea -- that's what IMAP is for -- but some people insist upon losing...
  2892.    */
  2893.                                 /* try to get the lock */
  2894.     if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) switch (errno) {
  2895.     case EEXIST:                /* if extant and old, grab it for ourselves */
  2896.       if ((!stat (lock,&sb)) && tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)
  2897.         ld = open (lock,O_WRONLY|O_CREAT,0666);
  2898.       break;
  2899.     case EACCES:                /* protection fail, ignore if non-ex or old */
  2900.       if (stat (lock,&sb) || (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60))
  2901.         *lock = '\0';           /* assume no world write mail spool dir */
  2902.       break;
  2903.     default:                    /* some other failure */
  2904.       sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  2905.       mm_log (tmp,WARN);        /* this is probably not good */
  2906.       *lock = '\0';             /* don't use lock files */
  2907.       break;
  2908.     }
  2909.     if (ld >= 0) {              /* if made a lock file */
  2910.       chmod (tmp,0666);         /* make sure others can break the lock */
  2911.       close (ld);               /* close the lock file */
  2912.     }
  2913. #endif
  2914.     if ((ld < 0) && *lock) {    /* if failed to make lock file and retry OK */
  2915.       if (!(i%15)) {
  2916.         sprintf (carmel_20k_buf,"Mailbox %s is locked, will override in %d seconds...",
  2917.                  file,i);
  2918.         mm_log (carmel_20k_buf, WARN);
  2919.       }
  2920.       sleep (1);                /* wait 1 second before next try */
  2921.     }
  2922.   } while (i-- && ld < 0 && *lock);
  2923.                                 /* open file */
  2924.   if ((fd = open (spool, O_RDONLY)) >= 0) flock (fd, LOCK_SH);
  2925.   else {                        /* open failed */
  2926.     j = errno;                  /* preserve error code */
  2927.     if (*lock) unlink (lock);   /* flush the lock file if any */
  2928.     errno = j;                  /* restore error code */
  2929.   }
  2930.   return(fd);
  2931. }
  2932.  
  2933.  
  2934.  
  2935. /*----------------------------------------------------------------------
  2936.    Berkeley unlock and close mailbox
  2937.  ----*/
  2938. static void 
  2939. carmel2_bezerk_unlock (fd, spool)
  2940.         int fd;
  2941.         char *spool;
  2942. {
  2943.     sprintf(carmel_path_buf, "%s.lock", spool);
  2944.     
  2945.     flock (fd, LOCK_UN);          /* release flock'ers */
  2946.     close (fd);                   /* close the file */
  2947.                                   /* flush the lock file if any */
  2948.     if (*carmel_path_buf) unlink (carmel_path_buf);
  2949. }
  2950.  
  2951.  
  2952.  
  2953. /*----------------------------------------------------------------------
  2954.     Make sure directory exists and is writable
  2955.  
  2956. Args: dir  - directory to check, should be full path
  2957.       
  2958. Result: returns -1 if not OK along with mm_logging a message
  2959.                  0 if OK
  2960. ----*/
  2961.  
  2962. carmel2_check_dir(dir)
  2963.      char *dir;
  2964. {
  2965.     struct stat sb;
  2966.     char   error_mess[150];
  2967.  
  2968.     if(stat(dir, &sb) < 0) {
  2969.         if(mkdir(dir, 0700) < 0) {
  2970.             sprintf(error_mess, "Error creating directory %-.30s %s",
  2971.                     dir, strerror(errno));
  2972.             mm_log(error_mess, WARN);
  2973.             return(-1);
  2974.         }
  2975.     } else if(!(sb.st_mode & S_IFDIR)) {
  2976.         sprintf(error_mess, "Warning: %s is not a directory",dir);
  2977.         mm_log(error_mess, WARN);
  2978.         return(-1);
  2979.  
  2980.     } else if(access(dir, W_OK) != 0) {
  2981.         sprintf(error_mess, "Warning: unable to write to %-.30s %s",
  2982.                 dir, strerror(errno));
  2983.         mm_log(error_mess, WARN);
  2984.         return(-1);
  2985.     }
  2986.     return(0);
  2987. }
  2988.  
  2989.     
  2990.     
  2991. /*----------------------------------------------------------------------
  2992.     Return the number for the next message file
  2993.  
  2994. Args: stream  -- the Mail stream for the new data file
  2995.       mailbox -- The FQN of mailbox data file is for
  2996.  
  2997. Returns: file number or -1
  2998.   ----*/
  2999. static 
  3000. carmel2_new_data_file(local, mailbox)
  3001.      CARMEL2LOCAL *local;
  3002.      char         *mailbox;
  3003. {
  3004.     char *path, num_buf[50], e_buf[100];
  3005.     int   fd, num, bytes_read;
  3006.  
  3007.     /*---- Get the full path of the .MAXNAME file for index ----*/
  3008.     path = (*local->calc_paths)(CalcPathCarmel2MAXNAME, mailbox, 0);
  3009.     if(path == NULL)
  3010.       return(-1);
  3011.  
  3012.     fd = open(path, O_RDWR|O_CREAT, 0666);
  3013.     if(fd < 0) {
  3014.         sprintf(e_buf, "Error getting next number of mail file: %s",
  3015.                 strerror(errno));
  3016.         mm_log(e_buf, ERROR);
  3017.         return(-1);
  3018.     }
  3019.  
  3020.     bytes_read = read(fd, num_buf, sizeof(num_buf));
  3021.     if(bytes_read < 6) {
  3022.         num  = 100000;
  3023.     } else {
  3024.         num = atoi(num_buf) + 1;
  3025.         if(num >= 999999)
  3026.           num = 100000;
  3027.     }
  3028.     sprintf(num_buf, "%-6d\n", num);
  3029.     lseek(fd, 0, 0);
  3030.     write(fd, num_buf, strlen(num_buf));
  3031.     close(fd);
  3032.     return(num);
  3033. }
  3034.  
  3035.  
  3036.  
  3037. /*----------------------------------------------------------------------
  3038.    Do all the file name generation for carmel2 driver
  3039.  
  3040.    The mailbox passed in is in either the format: #carmel2#folder or
  3041.                                                   #carmel2#user%folder
  3042. This generates that absolute paths for an index, or a data file or
  3043. the .MAXNAME file.
  3044.  
  3045. Bug: This is untested!
  3046.   ----*/
  3047. static char *
  3048. carmel2_calc_paths(operation, mailbox, data_file_num)
  3049.      int         operation;
  3050.      char       *mailbox;
  3051.      int         data_file_num;
  3052. {
  3053.     static char            path[CARMEL_PATHBUF_SIZE], num[20];
  3054.     char                  *p, *home_dir;
  3055.     struct carmel_mb_name *parsed_name;
  3056.     struct passwd         *pw;
  3057.  
  3058.     parsed_name = carmel_parse_mb_name(mailbox,'2');
  3059.  
  3060.     if(parsed_name == NULL) {
  3061.         mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
  3062.         return(NULL);
  3063.     }
  3064.     
  3065.  
  3066.     if(parsed_name->user != NULL) {
  3067.         /*---- has a user in mailbox name -----*/
  3068.         pw = getpwnam(parsed_name->user);
  3069.         if(pw == NULL) {
  3070.             sprintf(carmel_error_buf,
  3071.                   "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
  3072.                     mailbox, parsed_name->user);
  3073.             mm_log(carmel_error_buf, ERROR);
  3074.             carmel_free_mb_name(parsed_name);
  3075.             return(0);
  3076.         }
  3077.         home_dir = pw->pw_dir;
  3078.     } else {
  3079.         home_dir = myhomedir();
  3080.     }
  3081.     mailbox  = parsed_name->mailbox;
  3082.  
  3083.     switch(operation) {
  3084.  
  3085.       case CalcPathCarmel2Data:
  3086.         sprintf(path, "%s/%s/%d", home_dir, CARMEL2_MSG_DIR, data_file_num);
  3087.         
  3088.  
  3089.       case CalcPathCarmel2Index:
  3090.         sprintf(path, "%s/%s/%s", home_dir, CARMEL2_INDEX_DIR, mailbox);
  3091.         break;
  3092.  
  3093.       case CalcPathCarmel2MAXNAME:
  3094.         sprintf(path, "%s/%s", home_dir, CARMEL2_MAXFILE);
  3095.         break;
  3096.  
  3097.       case CalcPathCarmel2WriteLock:
  3098.         sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL2_INDEX_DIR, mailbox);
  3099.         break;
  3100.  
  3101.       case CalcPathCarmel2ReadLock:
  3102.         sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL2_INDEX_DIR, mailbox);
  3103.         break;
  3104.     }
  3105.  
  3106.     carmel_free_mb_name(parsed_name);
  3107.     return(path);
  3108. }
  3109.  
  3110.  
  3111.  
  3112. /*----------------------------------------------------------------------
  3113.    Find and parse the status line in a mail header
  3114.  
  3115. Args:  mc     -- the message cache where status is to be stored
  3116.        header -- the message header to parsen
  3117.  ----*/
  3118. void
  3119. carmel2_parse_bezerk_status(mc, header)
  3120.      MESSAGECACHE *mc;
  3121.      char *header;
  3122. {
  3123.     register char *p;
  3124.     for(p = header; *p; p++) {
  3125.         if(*p != '\n')
  3126.           continue;
  3127.         p++;
  3128.         if(*p != 'S' && *p != 'X')
  3129.           continue;
  3130.         if(strncmp(p, "Status: ",  8) == 0 || strncmp(p,"X-Status: ",10)== 0) {
  3131.             mc->recent = 1;
  3132.             for(p += *p == 'X' ? 10: 8; *p && *p != '\n'; p++)
  3133.               switch(*p) {
  3134.                 case 'R': mc->seen    = 1; break;
  3135.                 case 'O': mc->recent  = 0; break;
  3136.                 case 'D': mc->deleted = 1; break;
  3137.                 case 'F': mc->flagged = 1; break;
  3138.                 case 'A': mc->flagged = 1; break;
  3139.               }
  3140.         }
  3141.     }
  3142. }
  3143.         
  3144.  
  3145.  
  3146. /*----------------------------------------------------------------------
  3147.   Turn a string of describing flags into a bit mask
  3148.  
  3149. Args:  stream -- mail stream, unused
  3150.        flag   -- string flag list
  3151.  
  3152. Returns:  returns short with bits set; bits are defined in carmel2.h
  3153.  ----*/
  3154. static short
  3155. carmel2_getflags (stream, flag)
  3156.         MAILSTREAM *stream;
  3157.         char *flag;
  3158. {
  3159.   char *t, tmp[100];
  3160.   short f = 0;
  3161.   short i,j;
  3162.   if (flag && *flag) {          /* no-op if no flag string */
  3163.                                 /* check if a list and make sure valid */
  3164.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  3165.       mm_log ("Bad flag list",ERROR);
  3166.       return NIL;
  3167.     }
  3168.                                 /* copy the flag string w/o list construct */
  3169.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  3170.     tmp[j] = '\0';
  3171.     t = ucase (tmp);    /* uppercase only from now on */
  3172.  
  3173.     while (*t) {                /* parse the flags */
  3174.       if (*t == '\\') {         /* system flag? */
  3175.         switch (*++t) {         /* dispatch based on first character */
  3176.         case 'S':               /* possible \Seen flag */
  3177.           if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  3178.           t += 4;               /* skip past flag name */
  3179.           break;
  3180.         case 'D':               /* possible \Deleted flag */
  3181.           if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  3182.               t[5] == 'E' && t[6] == 'D') i = fDELETED;
  3183.           t += 7;               /* skip past flag name */
  3184.           break;
  3185.         case 'F':               /* possible \Flagged flag */
  3186.           if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  3187.               t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  3188.           t += 7;               /* skip past flag name */
  3189.           break;
  3190.         case 'A':               /* possible \Answered flag */
  3191.           if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  3192.               t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  3193.           t += 8;               /* skip past flag name */
  3194.           break;
  3195.         case 'R':               /* possible \Answered flag */
  3196.           if (t[1] == 'E' && t[2] == 'C' && t[3] == 'E' && t[4] == 'N' &&
  3197.               t[5] == 'T') i = fRECENT;
  3198.           t += 6;               /* skip past flag name */
  3199.           break;
  3200.         default:                /* unknown */
  3201.           i = 0;
  3202.           break;
  3203.         }
  3204.                                 /* add flag to flags list */
  3205.         if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  3206.         else {                  /* bitch about bogus flag */
  3207.           mm_log ("Unknown system flag",ERROR);
  3208.           return NIL;
  3209.         }
  3210.       }
  3211.       else {                    /* no user flags yet */
  3212.         mm_log ("Unknown flag",ERROR);
  3213.         return NIL;
  3214.       }
  3215.     }
  3216.   }
  3217.   return f;
  3218. }
  3219.  
  3220.  
  3221. /*----------------------------------------------------------------------
  3222.    Get a pointer to a MESSAGECACHE entry, allocating if needed
  3223.  
  3224. Args: stream -- message stream
  3225.       num    -- Message number to allocate on for
  3226.  
  3227. Returns: The MESSAGECACHE entry
  3228.  
  3229. The message caches are allocated in blocks of 256 to save space taken up by
  3230. pointers in a linked list and allocation overhead. The mc_blocks 
  3231. data structure is an array that points to each block. The macro MC() 
  3232. defined in carmel.h returns a pointer to a MESSAGECACHE given
  3233. a message number. This function here can be called when a MESSAGECACHE
  3234. is needed to a message number that might be new. It allocates a new
  3235. block if needed and clears the MESSAGECACHE returned.
  3236.  
  3237. The MESSAGECACHE entries are currently about 28 bytes which implies 28Kb
  3238. must be used per 1000 messages. If memory is limited this driver will be 
  3239. limited in the number of messages it can handle, and the limit is due to
  3240. the fact that these MESSAGECACHEs must be in core.
  3241.  
  3242. It might be possible to reduce the size of each entry by a few bytes if
  3243. the message numbers were reduced to a short, and the mostly unused keywords
  3244. were removed. It would also be nice to add a day of the week (3 bits)
  3245.   ----*/
  3246. static MESSAGECACHE *
  3247. carmel2_new_mc(stream, num)
  3248.      MAILSTREAM *stream;
  3249.      int num;
  3250. {
  3251.     MESSAGECACHE *mc;
  3252.       
  3253.     /* Make sure we've got a cache_entry */
  3254.     if(num >= LOCAL->cache_size) {
  3255.         if(LOCAL->mc_blocks == NULL)
  3256.           LOCAL->mc_blocks=(MESSAGECACHE **)fs_get(sizeof(MESSAGECACHE *));
  3257.         else
  3258.           fs_resize((void **)&(LOCAL->mc_blocks), ((num >>8) + 1) *
  3259.                                                   sizeof(MESSAGECACHE *));
  3260.         LOCAL->mc_blocks[num >> 8] = (MESSAGECACHE *)
  3261.           fs_get(256 * sizeof(MESSAGECACHE));
  3262.         LOCAL->cache_size = ((num >> 8) + 1) * 256;
  3263.     }
  3264.     
  3265.     mc = MC(num);
  3266.  
  3267.     mc->user_flags = 0;
  3268.     mc->lockcount  = 0;
  3269.     mc->seen       = 0;
  3270.     mc->deleted    = 0;
  3271.     mc->flagged    = 0;
  3272.     mc->answered   = 0;
  3273.     mc->recent     = 0;
  3274.     mc->searched   = 0;
  3275.     mc->sequence   = 0;
  3276.     mc->spare      = 0;
  3277.     mc->spare2     = 0;
  3278.     mc->spare3     = 0;
  3279.     mc->msgno      = num;
  3280.     
  3281.    /* Don't set the date, the size and the extra data,
  3282.       assume they will be set 
  3283.     */
  3284.     return(mc);
  3285. }
  3286.  
  3287.  
  3288.  
  3289. /*----------------------------------------------------------------------
  3290.   Do the real work of appending a message to a mailbox
  3291.  
  3292. Args: local -- The carmel2 local data structure, (a some what fake incomplete
  3293.                one, set up for the purposes here)
  3294.       mailbox -- Name of mailbox to append to
  3295.       message -- The rfc822 format of the message with \r\n's
  3296.  
  3297. Returns: T if all went OK, NIL if it did not
  3298.  
  3299.  - Make sure index exists or can be created
  3300.  - lock index for write
  3301.  - get a data file name
  3302.  - Put the text in the file
  3303.  - Parse the string to get envelope and message cache
  3304.  - Write the entry into the index
  3305.  - Unlock the index
  3306.  
  3307. BUG: This needs some locking and some error messages
  3308.  
  3309.   ----*/
  3310. carmel2_append2(stream, local, mailbox, flags, date, message)
  3311.      char         *mailbox, *flags, *date;
  3312.      CARMEL2LOCAL *local;
  3313.      STRING       *message;
  3314.      MAILSTREAM   *stream;
  3315. {
  3316.     char         *index_name, *data_name, *p, c, *header_string;
  3317.     ENVELOPE     *envelope;
  3318.     BODY         *b;
  3319.     MESSAGECACHE  mc;
  3320.     FILE         *index_file, *data_file;
  3321.     struct stat   sb;
  3322.     int           last_was_crlf, saved_errno;
  3323.     STRING        string_struct;
  3324.     long          size;
  3325.     short         flagbits;
  3326.  
  3327.     /*------ Lock the mailbox for write ------*/
  3328.     if(carmel2_lock(local, mailbox, WRITE_LOCK) < 0) {
  3329.         sprintf(carmel_error_buf,
  3330.                 "Mailbox \"%s\" is locked. Can't append to it.",
  3331.                 mailbox);
  3332.         mm_log(carmel_error_buf, ERROR);
  3333.         return(NIL);
  3334.     }
  3335.  
  3336.     /*----- Get the file name of carmel2 index -----*/
  3337.     index_name = (*local->calc_paths)(CalcPathCarmel2Index, mailbox, 0);
  3338.     if(index_name == NULL) {
  3339.         saved_errno = 0;
  3340.         goto bomb;
  3341.     }
  3342.  
  3343.     /*------ See if it exists already or not ------*/
  3344.     if(stat(index_name, &sb) < 0) {
  3345.         mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
  3346.         carmel2_unlock(local, mailbox, WRITE_LOCK);
  3347.         return(NIL);
  3348.     }
  3349.  
  3350.     index_file = fopen(index_name, "a");
  3351.     if(index_file == NULL)
  3352.       goto bomb;
  3353.  
  3354.     mc.data2 = carmel2_new_data_file(local, mailbox);
  3355.  
  3356.     flagbits = carmel2_getflags(NULL, flags);
  3357.  
  3358.     if(flagbits & fSEEN)     mc.seen     = T;
  3359.     if(flagbits & fDELETED)  mc.deleted  = T;
  3360.     if(flagbits & fFLAGGED)  mc.flagged  = T;
  3361.     if(flagbits & fANSWERED) mc.answered = T;
  3362.     if(flagbits & fRECENT)   mc.recent   = T;
  3363.     mc.user_flags  = 0;
  3364.  
  3365.     /*----- Open the data file -----*/
  3366.     data_name = (*local->calc_paths)(CalcPathCarmel2Data, mailbox, mc.data2);
  3367.     if(data_name == NULL)  {
  3368.         errno = 0; /* Don't generate an error message at all */
  3369.         goto bomb;
  3370.     }
  3371.     data_file = fopen(data_name, "w");
  3372.     if(data_file == NULL)
  3373.       goto bomb; 
  3374.  
  3375.     /*--- accumulate header as we go for later parsing ---*/
  3376.     header_string = carmel_20k_buf;
  3377.  
  3378.     /*------ Write the message to the file, and get header in a string -----*/
  3379.     for(size = SIZE(message); size > 0; size--){
  3380.         c = SNX(message);
  3381.         if(c == '\r' && size > 1) {
  3382.             /* Turn CRLF into LF for UNIX */
  3383.             c = SNX(message);
  3384.             size--;
  3385.             if(c != '\n') {
  3386.                 if(fputc('\r', data_file) < 0 || fputc(c, data_file) < 0) 
  3387.                   goto bomb;
  3388.                 if(header_string != NULL) {
  3389.                     *header_string++ = '\r';
  3390.                     *header_string++ = c;
  3391.                 }
  3392.             } else {
  3393.                 if(fputc('\n', data_file) < 0)
  3394.                   goto bomb;
  3395.                 if(header_string != NULL) {
  3396.                     if(last_was_crlf) {
  3397.                         *header_string = '\0';
  3398.                         header_string = NULL;
  3399.                     }  else {
  3400.                       *header_string++ = '\r';
  3401.                       *header_string++ = '\n';
  3402.                     }
  3403.                 }
  3404.                 last_was_crlf = 1;
  3405.               }
  3406.         } else {
  3407.             if(fputc(c, data_file) == EOF) 
  3408.               goto bomb;
  3409.             if(header_string != NULL)
  3410.               *header_string++ = c;
  3411.             last_was_crlf = 0;
  3412.         }
  3413.     }
  3414.     if(fclose(data_file) == EOF) 
  3415.       goto bomb;
  3416.     data_file = NULL;
  3417.  
  3418.     
  3419.     /*----Get the size that we actually wrote -----*/
  3420.     stat(data_name, &sb);
  3421.     mc.rfc822_size = sb.st_size;
  3422.  
  3423.     /* This blows the nice tight memory usage for the carmel2 driver :-( */
  3424.     header_string = cpystr(carmel_20k_buf); 
  3425.  
  3426. #ifdef BWC
  3427.     /*--- For MIME type x-bwc-glyph-wide, store in a nnnn.wid file ----*/
  3428.     for(p = header_string; *p; p++) {
  3429.         if((p == header_string  && carmel_match_glyph_wide(p)) ||
  3430.            (*p == '\r' && *(p+1) == '\n' && carmel_match_glyph_wide(p+2))) {
  3431.             sprintf(carmel_path_buf, "%s.wid", data_name);
  3432.             rename(data_name, carmel_path_buf);
  3433.             break;
  3434.         }
  3435.     }
  3436. #endif
  3437.  
  3438.     /*------ Parse the message to get envelope and message cache ----*/
  3439.     INIT(&string_struct, mail_string, (void *)"", 0);
  3440.     rfc822_parse_msg(&envelope, &b, header_string, strlen(header_string),
  3441.                      &string_struct, mylocalhost(), carmel_20k_buf);
  3442.     carmel2_parse_bezerk_status(&mc, header_string);
  3443.     carmel2_rfc822_date(&mc, header_string);
  3444.  
  3445.     /*------ Write the entry into the index ------*/
  3446.     if(carmel2_write_index(envelope, &mc, index_file) < 0)
  3447.       goto bomb;
  3448.  
  3449.     if(local->aux_copy != NULL) /* Write carmel index if needed */
  3450.       (*local->aux_copy)(local, mailbox, envelope, &mc);
  3451.  
  3452.     mail_free_envelope(&envelope);
  3453.     fs_give((void **)&header_string);
  3454.  
  3455.     if(fclose(index_file) == EOF)
  3456.       goto bomb;
  3457.     carmel2_unlock(local, mailbox, WRITE_LOCK);
  3458.     return(T);
  3459.  
  3460.   bomb:
  3461.     saved_errno = errno;
  3462.     if(index_file != NULL) {
  3463.         fclose(index_file);
  3464.     }
  3465.     if(data_file != NULL) {
  3466.         fclose(data_file);
  3467.         unlink(data_name);
  3468.     }
  3469.     carmel2_unlock(local, mailbox, WRITE_LOCK);
  3470.     if(saved_errno != 0) {
  3471.         sprintf(carmel_error_buf,"Message append failed: %s",
  3472.                 strerror(saved_errno));
  3473.         mm_log(carmel_error_buf, ERROR);
  3474.     }
  3475.     return(NIL);
  3476. }
  3477.  
  3478.  
  3479.  
  3480.  
  3481. /* Search support routines
  3482.  * Accepts: MAIL stream
  3483.  *          message number
  3484.  *          pointer to additional data
  3485.  *          pointer to temporary buffer
  3486.  * Returns: T if search matches, else NIL
  3487.  */
  3488.  
  3489. static char 
  3490. carmel2_search_all (stream,msgno,d,n)
  3491.         MAILSTREAM *stream;
  3492.         long msgno;
  3493.         char *d;
  3494.         long n;
  3495. {
  3496.   return T;                     /* ALL always succeeds */
  3497. }
  3498.  
  3499.  
  3500. static char 
  3501. carmel2_search_answered (stream,msgno,d,n)
  3502.         MAILSTREAM *stream;
  3503.         long msgno;
  3504.         char *d;
  3505.         long n;
  3506. {
  3507.   return MC(msgno)->answered ? T : NIL;
  3508. }
  3509.  
  3510.  
  3511. static char 
  3512. carmel2_search_deleted (stream,msgno,d,n)
  3513.         MAILSTREAM *stream;
  3514.         long msgno;
  3515.         char *d;
  3516.         long n;
  3517. {
  3518.   return MC(msgno)->deleted ? T : NIL;
  3519. }
  3520.  
  3521.  
  3522. static char 
  3523. carmel2_search_flagged (stream,msgno,d,n)
  3524.         MAILSTREAM *stream;
  3525.         long msgno;
  3526.         char *d;
  3527.         long n;
  3528. {
  3529.   return MC(msgno)->flagged ? T : NIL;
  3530. }
  3531.  
  3532.  
  3533. static char 
  3534. carmel2_search_keyword (stream,msgno,d,n)
  3535.         MAILSTREAM *stream;
  3536.         long msgno;
  3537.         char *d;
  3538.         long n;
  3539. {
  3540.   return NIL;                   /* keywords not supported yet */
  3541. }
  3542.  
  3543.  
  3544. static char 
  3545. carmel2_search_new (stream,msgno,d,n)
  3546.         MAILSTREAM *stream;
  3547.         long msgno;
  3548.         char *d;
  3549.         long n;
  3550. {
  3551.   MESSAGECACHE *elt = MC(msgno);
  3552.   return (elt->recent && !elt->seen) ? T : NIL;
  3553. }
  3554.  
  3555. static char 
  3556. carmel2_search_old (stream,msgno,d,n)
  3557.         MAILSTREAM *stream;
  3558.         long msgno;
  3559.         char *d;
  3560.         long n;
  3561. {
  3562.   return MC(msgno)->recent ? NIL : T;
  3563. }
  3564.  
  3565.  
  3566. static char 
  3567. carmel2_search_recent (stream,msgno,d,n)
  3568.         MAILSTREAM *stream;
  3569.         long msgno;
  3570.         char *d;
  3571.         long n;
  3572. {
  3573.   return MC(msgno)->recent ? T : NIL;
  3574. }
  3575.  
  3576.  
  3577. static char 
  3578. carmel2_search_seen (stream,msgno,d,n)
  3579.         MAILSTREAM *stream;
  3580.         long msgno;
  3581.         char *d;
  3582.         long n;
  3583. {
  3584.   return MC(msgno)->seen ? T : NIL;
  3585. }
  3586.  
  3587.  
  3588. static char 
  3589. carmel2_search_unanswered (stream,msgno,d,n)
  3590.         MAILSTREAM *stream;
  3591.         long msgno;
  3592.         char *d;
  3593.         long n;
  3594. {
  3595.   return MC(msgno)->answered ? NIL : T;
  3596. }
  3597.  
  3598.  
  3599. static char 
  3600. carmel2_search_undeleted (stream,msgno,d,n)
  3601.         MAILSTREAM *stream;
  3602.         long msgno;
  3603.         char *d;
  3604.         long n;
  3605. {
  3606.   return MC(msgno)->deleted ? NIL : T;
  3607. }
  3608.  
  3609.  
  3610. static char 
  3611. carmel2_search_unflagged (stream,msgno,d,n)
  3612.         MAILSTREAM *stream;
  3613.         long msgno;
  3614.         char *d;
  3615.         long n;
  3616. {
  3617.   return MC(msgno)->flagged ? NIL : T;
  3618. }
  3619.  
  3620.  
  3621. static char 
  3622. carmel2_search_unkeyword (stream,msgno,d,n)
  3623.         MAILSTREAM *stream;
  3624.         long msgno;
  3625.         char *d;
  3626.         long n;
  3627. {
  3628.   return T;                     /* keywords not supported yet */
  3629. }
  3630.  
  3631.  
  3632. static char 
  3633. carmel2_search_unseen (stream,msgno,d,n)
  3634.         MAILSTREAM *stream;
  3635.         long msgno;
  3636.         char *d;
  3637.         long n;
  3638. {
  3639.   return MC(msgno)->seen ? NIL : T;
  3640. }
  3641.  
  3642. static char 
  3643. carmel2_search_before (stream,msgno,d,n)
  3644.         MAILSTREAM *stream;
  3645.         long msgno;
  3646.         char *d;
  3647.         long n;
  3648. {
  3649.   return (char) (carmel2_msgdate (stream,msgno) < n);
  3650. }
  3651.  
  3652.  
  3653. static char 
  3654. carmel2_search_on (stream,msgno,d,n)
  3655.         MAILSTREAM *stream;
  3656.         long msgno;
  3657.         char *d;
  3658.         long n;
  3659. {
  3660.   return (char) (carmel2_msgdate (stream,msgno) == n);
  3661. }
  3662.  
  3663.  
  3664. static char 
  3665. carmel2_search_since (stream,msgno,d,n)
  3666.         MAILSTREAM *stream;
  3667.         long msgno;
  3668.         char *d;
  3669.         long n;
  3670. {
  3671.                                 /* everybody interprets "since" as .GE. */
  3672.   return (char) (carmel2_msgdate (stream,msgno) >= n);
  3673. }
  3674.  
  3675.  
  3676. static unsigned long 
  3677. carmel2_msgdate (stream,msgno)
  3678.         MAILSTREAM *stream;
  3679.         long msgno;
  3680. {
  3681.   struct stat sbuf;
  3682.   struct tm *tm;
  3683.   MESSAGECACHE *elt = MC(msgno);
  3684.  
  3685.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  3686. }
  3687.  
  3688. /*----------------------------------------------------------------------
  3689.     Search only the body of the text.
  3690.   BUG, probably need to unencode before searching
  3691.   ---*/
  3692. static char 
  3693. carmel2_search_body (stream,msgno, pat, pat_len)
  3694.         MAILSTREAM *stream;
  3695.         long msgno,pat_len;
  3696.         char *pat;
  3697. {
  3698.   char *t =  carmel2_fetchtext(stream, msgno);
  3699.   return (t && search (t,strlen(t), pat, pat_len));
  3700. }
  3701.  
  3702.  
  3703. /*----------------------------------------------------------------------
  3704.     Search the subject field
  3705.   ----*/
  3706. static char 
  3707. carmel2_search_subject (stream,msgno, pat, pat_len)
  3708.         MAILSTREAM *stream;
  3709.         long msgno, pat_len;
  3710.         char *pat;
  3711. {
  3712.   char *t = carmel2_fetchstructure (stream,msgno,NULL)->subject;
  3713.   return t ? search (t, strlen(t), pat, pat_len) : NIL;
  3714. }
  3715.  
  3716.  
  3717. /*----------------------------------------------------------------------
  3718.     Search the full header and body text of the message
  3719.   ---*/
  3720. static char 
  3721. carmel2_search_text (stream, msgno, pat, pat_len)
  3722.         MAILSTREAM *stream;
  3723.         long        msgno, pat_len;
  3724.         char       *pat;
  3725. {
  3726.   char *t = carmel2_fetchheader(stream,msgno);
  3727.   return (t && search(t, strlen(t), pat, pat_len)) ||
  3728.     carmel2_search_body(stream,msgno, pat, pat_len);
  3729. }
  3730.  
  3731.  
  3732. /*----------------------------------------------------------------------
  3733.    Search the Bcc field
  3734.   ---*/
  3735. static char 
  3736. carmel2_search_bcc (stream,msgno,d,n)
  3737.         MAILSTREAM *stream;
  3738.         long msgno;
  3739.         char *d;
  3740.         long n;
  3741. {
  3742.   carmel_20k_buf[0] = '\0';
  3743.                                 /* get text for address */
  3744.   rfc822_write_address (carmel_20k_buf,
  3745.                         carmel2_fetchstructure (stream,msgno,NULL)->bcc);
  3746.   return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
  3747. }
  3748.  
  3749.  
  3750. static char 
  3751. carmel2_search_cc (stream,msgno,d,n)
  3752.         MAILSTREAM *stream;
  3753.         long msgno;
  3754.         char *d;
  3755.         long n;
  3756. {
  3757.   carmel_20k_buf[0] = '\0';
  3758.                                 /* get text for address */
  3759.   rfc822_write_address (carmel_20k_buf,
  3760.                         carmel2_fetchstructure (stream, msgno, NULL)->cc);
  3761.   return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
  3762. }
  3763.  
  3764.  
  3765. static char 
  3766. carmel2_search_from (stream,msgno,d,n)
  3767.         MAILSTREAM *stream;
  3768.         long msgno;
  3769.         char *d;
  3770.         long n;
  3771. {
  3772.   carmel_20k_buf[0] = '\0';
  3773.                                 /* get text for address */
  3774.   rfc822_write_address (carmel_20k_buf,
  3775.                         carmel2_fetchstructure (stream,msgno,NULL)->from);
  3776.   return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
  3777. }
  3778.  
  3779.  
  3780. static char 
  3781. carmel2_search_to (stream,msgno, pat, pat_len)
  3782.         MAILSTREAM *stream;
  3783.         long msgno;
  3784.         char *pat;
  3785.         long pat_len;
  3786. {
  3787.   carmel_20k_buf[0] = '\0';
  3788.                                 /* get text for address */
  3789.   rfc822_write_address (carmel_20k_buf,
  3790.                         carmel2_fetchstructure (stream, msgno, NULL)->to);
  3791.   return(search(carmel_20k_buf,strlen(carmel_20k_buf), pat, pat_len));
  3792. }
  3793.  
  3794. /* Search parsers */
  3795.  
  3796.  
  3797. /* Parse a date
  3798.  * Accepts: function to return
  3799.  *          pointer to date integer to return
  3800.  * Returns: function to return
  3801.  */
  3802.  
  3803. static search_t
  3804. carmel2_search_date (f, n)
  3805.         search_t f;
  3806.         long *n;
  3807. {
  3808.   long i;
  3809.   char *s;
  3810.   MESSAGECACHE elt;
  3811.                                 /* parse the date and return fn if OK */
  3812.   return (carmel2_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  3813.           (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  3814. }
  3815.  
  3816. /* Parse a flag
  3817.  * Accepts: function to return
  3818.  *          pointer to string to return
  3819.  * Returns: function to return
  3820.  */
  3821.  
  3822. static search_t 
  3823. carmel2_search_flag (f,d)
  3824.         search_t f;
  3825.         char **d;
  3826. {
  3827.                                 /* get a keyword, return if OK */
  3828.   return (*d = strtok (NIL," ")) ? f : NIL;
  3829. }
  3830.  
  3831.  
  3832. /* Parse a string
  3833.  * Accepts: function to return
  3834.  *          pointer to string to return
  3835.  *          pointer to string length to return
  3836.  * Returns: function to return
  3837.  */
  3838.  
  3839. static search_t 
  3840. carmel2_search_string (f,d,n)
  3841.         search_t f;
  3842.         char **d;
  3843.         long *n;
  3844. {
  3845.   char *c = strtok (NIL,"");    /* remainder of criteria */
  3846.   if (c) {                      /* better be an argument */
  3847.     switch (*c) {               /* see what the argument is */
  3848.     case '\0':                  /* catch bogons */
  3849.     case ' ':
  3850.       return NIL;
  3851.     case '"':                   /* quoted string */
  3852.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  3853.         return NIL;
  3854.       break;
  3855.     case '{':                   /* literal string */
  3856.       *n = strtol (c+1,&c,10);  /* get its length */
  3857.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  3858.           *n > strlen (*d = c)) return NIL;
  3859.       c[*n] = '\255';           /* write new delimiter */
  3860.       strtok (c,"\255");        /* reset the strtok mechanism */
  3861.       break;
  3862.     default:                    /* atomic string */
  3863.       *n = strlen (*d = strtok (c," "));
  3864.       break;
  3865.     }
  3866.     return f;
  3867.   }
  3868.   else return NIL;
  3869. }
  3870.  
  3871.  
  3872.  
  3873.  
  3874.  
  3875.    
  3876. /*----------------------------------------------------------------------
  3877.    Some stuff to help out with the date parsing
  3878.  ---*/  
  3879. char *xdays2[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
  3880.  
  3881. char *
  3882. month_abbrev2(month_num)
  3883.      int month_num;
  3884. {
  3885.     static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  3886.                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
  3887.     if(month_num < 1 || month_num > 12)
  3888.       return("xxx");
  3889.     return(xmonths[month_num - 1]);
  3890. }
  3891.  
  3892. struct time_zone_names {
  3893.     char *name;
  3894.     int   hours;
  3895.     int    minutes;
  3896. } tz_names[]  = {
  3897.     {"GMT",   0,   0},
  3898.     {"PST",  -8,   0},
  3899.     {"PDT",  -7,   0},
  3900.     {"MST",  -7,   0},
  3901.     {"MDT",  -6,   0},
  3902.     {"CST",  -6,   0},
  3903.     {"CDT",  -5,   0},
  3904.     {"EST",  -5,   0},
  3905.     {"EDT",  -4,   0},
  3906.     {"JST",   9,   0},
  3907.     {"IST",   2,   0},
  3908.     {"IDT",   3,   0},
  3909.     {NULL,    0,   0}};
  3910.     
  3911. /*----------------------------------------------------------------------
  3912.    Parse a date string into into a structure
  3913.  
  3914. Args: mc -- mesage cache to with structure to receive data
  3915.       given_date -- full header with date string somewhere to be found
  3916.  
  3917. This parses a number of date formats and produces a cannonical date in
  3918. a structure. The basic format is:
  3919.    
  3920.  dd mm yy hh:mm:ss.t tz   
  3921.  
  3922. It will also handle:
  3923.   ww dd mm yy hh:mm:ss.t tz     mm dd yy hh:mm:ss.t tz
  3924.   ww dd mm hh:mm:ss.t yy tz     mm dd hh:mm:ss.t yy tz
  3925.  
  3926. It knows many abbreviations for timezones, but is not complete.
  3927. In general absolute time zones in hours +/- GMT are best.
  3928.   ----*/
  3929. void
  3930. carmel2_rfc822_date(mc, given_date)
  3931.      char        *given_date;
  3932.      MESSAGECACHE *mc;
  3933. {
  3934.     char *p, **i, *q;
  3935.     int   month, year;
  3936.     struct time_zone_names *tz;
  3937.  
  3938.     mc->seconds  = 0;
  3939.     mc->minutes  = 0;
  3940.     mc->hours    = 30;
  3941.     mc->day      = 0;
  3942.     mc->month    = 0;
  3943.     mc->year     = 0;
  3944.     mc->zhours   = 0;
  3945.     mc->zminutes = 0;
  3946.  
  3947.     if(given_date == NULL)
  3948.       return;
  3949.  
  3950.     p = given_date;
  3951.  
  3952.     if(*p != 'D' && strncmp(p, "Date:",5))
  3953.       while(*p) {
  3954.           if(*p == '\n' && *(p+1) == 'D' && strncmp(p+1, "Date:", 5) == 0)
  3955.             break;
  3956.          p++;
  3957.       }
  3958.     if(!*p)
  3959.       return;
  3960.  
  3961.     p += 6; /* Skip "\nDate: " */
  3962.     while(isspace(*p))
  3963.       p++;
  3964.            
  3965.     /* Start with month, weekday or day ? */
  3966.     for(i = xdays2; *i != NULL; i++) 
  3967.       if(struncmp2(p, *i, 3) == 0) /* Match first 3 letters */
  3968.         break;
  3969.     if(*i != NULL) {
  3970.         /* Started with week day .. buz over it*/
  3971.         while(*p && !isspace(*p) && *p != ',')
  3972.           p++;
  3973.         while(*p && (isspace(*p) || *p == ','))
  3974.           p++;
  3975.     }
  3976.     if(isdigit(*p)) {
  3977.         mc->day = atoi(p);
  3978.         while(*p && isdigit(*p))
  3979.           p++;
  3980.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  3981.           p++;
  3982.     }
  3983.     for(month = 1; month <= 12; month++)
  3984.       if(struncmp2(p, month_abbrev2(month), 3) == 0)
  3985.         break;
  3986.     if(month < 13) {
  3987.         mc->month = month;
  3988.  
  3989.     } 
  3990.     /* Move over month, (or whatever is there) */
  3991.     while(*p && !isspace(*p) && *p != ',' && *p != '-')
  3992.        p++;
  3993.     while(*p && (isspace(*p) || *p == ',' || *p == '-'))
  3994.        p++;
  3995.  
  3996.     /* Check again for day */
  3997.     if(isdigit(*p) && mc->day == -1) {
  3998.         mc->day = atoi(p);
  3999.         while(*p && isdigit(*p))
  4000.           p++;
  4001.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  4002.           p++;
  4003.     }
  4004.  
  4005.     /*-- Check for time --*/
  4006.     for(q = p; *q && isdigit(*q); q++);
  4007.     if(*q == ':') {
  4008.         /* It's the time (out of place) */
  4009.         mc->hours = atoi(p);
  4010.         while(*p && *p != ':' && !isspace(*p))
  4011.           p++;
  4012.         if(*p == ':') {
  4013.             mc->minutes = atoi(p);
  4014.             while(*p && *p != ':' && !isspace(*p))
  4015.               p++;
  4016.             if(*p == ':') {
  4017.                 mc->seconds = atoi(p);
  4018.                 while(*p && !isspace(*p))
  4019.                   p++;
  4020.             }
  4021.         }
  4022.         while(*p && isspace(*p))
  4023.           p++;
  4024.     }
  4025.     
  4026.     /* Get the year 0-50 is 2000-2050; 50-100 is 1950-1999 and
  4027.                                            101-9999 is 101-9999 */
  4028.     if(isdigit(*p)) {
  4029.         year = atoi(p);
  4030.         if(year < 50)   
  4031.           year += 2000;
  4032.         else if(year < 100)
  4033.           year += 1900;
  4034.         mc->year = year - 1969; 
  4035.         while(*p && isdigit(*p))
  4036.           p++;
  4037.         while(*p && (*p == '-' || *p == ',' || isspace(*p)))
  4038.           p++;
  4039.     } else {
  4040.         /* Something wierd, skip it and try to resynch */
  4041.         while(*p && !isspace(*p) && *p != ',' && *p != '-')
  4042.           p++;
  4043.         while(*p && (isspace(*p) || *p == ',' || *p == '-'))
  4044.           p++;
  4045.     }
  4046.  
  4047.     /*-- Now get hours minutes, seconds and ignore tenths --*/
  4048.     for(q = p; *q && isdigit(*q); q++);
  4049.     if(*q == ':' && mc->hours == 30) {
  4050.         mc->hours = atoi(p);
  4051.         while(*p && *p != ':' && !isspace(*p))
  4052.           p++;
  4053.         if(*p == ':') {
  4054.             p++;
  4055.             mc->minutes = atoi(p);
  4056.             while(*p && *p != ':' && !isspace(*p))
  4057.               p++;
  4058.             if(*p == ':') {
  4059.                 p++;
  4060.                 mc->seconds = atoi(p);
  4061.                 while(*p && !isspace(*p) && *p != '+' && *p != '-')
  4062.                   p++;
  4063.             }
  4064.         }
  4065.     }
  4066.     while(*p && isspace(*p))
  4067.       p++;
  4068.  
  4069.  
  4070.     /*-- The time zone --*/
  4071.     if(*p) {
  4072.         if(*p == '+' || *p == '-') {
  4073.             char tmp[3];
  4074.             mc->zoccident = (*p == '+' ? 1 : 0);
  4075.             p++;
  4076.             tmp[0] = *p++;
  4077.             tmp[1] = *p++;
  4078.             tmp[2] = '\0';
  4079.             mc->zhours = atoi(tmp);
  4080.             tmp[0] = *p++;
  4081.             tmp[1] = *p++;
  4082.             tmp[2] = '\0';
  4083.             mc->zminutes *= atoi(tmp);
  4084.         } else {
  4085.             for(tz = tz_names; tz->name != NULL; tz++) {
  4086.                 if(struncmp2(p, tz->name, strlen(tz->name)) ==0) {
  4087.                     if(tz->hours >= 0) {
  4088.                         mc->zhours = tz->hours;
  4089.                         mc->zoccident = 1;
  4090.                     } else {
  4091.                         mc->zhours    = -tz->hours;
  4092.                         mc->zoccident = 0;
  4093.                     }
  4094.                     mc->zminutes = tz->minutes;
  4095.                     break;
  4096.                 }
  4097.             }
  4098.         }
  4099.     }
  4100.     if(mc->hours == 30)
  4101.       mc->hours = 0;
  4102. }
  4103.  
  4104.  
  4105.       
  4106. /*----------------------------------------------------------------------
  4107.     Print the date from the MESSAGECACHE into the string
  4108.  ----*/
  4109. static void
  4110. carmel2_date2string(string, mc)
  4111.      char         *string;
  4112.      MESSAGECACHE *mc;
  4113. {
  4114.     sprintf(string, "%d %s %d %d:%02d:%02d %s%04d",
  4115.             mc->day, month_abbrev2(mc->month), 1969+mc->year,
  4116.             mc->hours, mc->minutes, mc->seconds, mc->zoccident ? "-" : "",
  4117.             mc->zhours * 100 + mc->zminutes);
  4118. }
  4119.  
  4120.  
  4121.  
  4122. /*----------------------------------------------------------------------
  4123.     Read the date into a structure from line in Carmel index
  4124.  
  4125.  Args: mc     -- The structure to contain the date (and other things)
  4126.        string -- String to be parsed. Format is:
  4127.                   "yyyy^Amm^Add^Ahh^Amm^Ass^Azh^Azm"
  4128.  
  4129.  ----*/
  4130. static void
  4131. carmel2_parse_date(mc, string)
  4132.      MESSAGECACHE *mc;
  4133.      char         *string;
  4134. {
  4135.     int n;
  4136.     mc->year    = next_num(&string) - 1969;
  4137.     mc->month   = next_num(&string);
  4138.     mc->day     = next_num(&string);
  4139.     mc->hours   = next_num(&string);
  4140.     mc->minutes = next_num(&string);
  4141.     mc->seconds = next_num(&string);
  4142.  
  4143.     n = next_num(&string);
  4144.     if(n < 0) {
  4145.         mc->zoccident = 0;
  4146.         mc->zhours    = -n;
  4147.     } else {
  4148.         mc->zoccident = 1;
  4149.         mc->zhours    = n;
  4150.     }
  4151.     mc->zminutes = next_num(&string);      
  4152. }
  4153.  
  4154.  
  4155.  
  4156. /*----------------------------------------------------------------------
  4157.    Read the next number out of string and return it, advancing the string
  4158.   ----*/
  4159. static
  4160. next_num(string)
  4161.      char **string;
  4162. {
  4163.     int   n;
  4164.     char *p;
  4165.  
  4166.     if(string == NULL)
  4167.       return(0);
  4168.  
  4169.     p = *string;
  4170.     n = atoi(p);
  4171.     while(*p > '\001')
  4172.       p++;
  4173.     if(*p)
  4174.       *string = p+1;
  4175.     else
  4176.       *string = NULL;
  4177.     return(n);
  4178. }
  4179.  
  4180.  
  4181. /*----------------------------------------------------------------------
  4182.      Take a (slightly ugly) FQ mailbox and and return the prettier 
  4183.   last part of it
  4184.   ----*/
  4185. char *
  4186. carmel_pretty_mailbox(mailbox)
  4187.      char *mailbox;
  4188. {
  4189.     char *pretty_mb;
  4190.  
  4191.     for(pretty_mb = mailbox + strlen(mailbox) - 1;
  4192.         *pretty_mb != '#' && pretty_mb > mailbox;
  4193.         pretty_mb--)
  4194.       ;
  4195.     if(*pretty_mb == '#')
  4196.       pretty_mb++;
  4197.     return(pretty_mb);
  4198. }
  4199.  
  4200. /*----------------------------------------------------------------------
  4201.     Parse a carmel mailbox name into its parts
  4202.  
  4203. Args: fq_name: The name to parse
  4204.       given_version: The version that must match; currently either \0 or '2'
  4205.  
  4206. Returns: NULL if not a valid carmel name, version of name and given_version
  4207.          do not match, or a malloced structure if it is.
  4208.   ----*/
  4209. struct carmel_mb_name *
  4210. carmel_parse_mb_name(fq_name, given_version)
  4211.      char *fq_name;
  4212.      char  given_version;
  4213. {
  4214.     char                  *p, *q, version[2];
  4215.     struct carmel_mb_name *parsed_name;
  4216.  
  4217.     if(struncmp2(fq_name, CARMEL_NAME_PREFIX, strlen(CARMEL_NAME_PREFIX))!= 0){
  4218.         return(0);   /* BUG -- we won't work with non-FQN names for now */
  4219.         p = fq_name;
  4220.     version[0] = given_version;
  4221.     version[1] = '\0';
  4222.     } else {
  4223.     if(fq_name[7] == CARMEL_NAME_CHAR) {
  4224.         version[0] = '\0';
  4225.         p = fq_name + 8;
  4226.     } else if(fq_name[8] == CARMEL_NAME_CHAR) {
  4227.         version[0] = fq_name[7];
  4228.         version[1] = '\0';
  4229.         p = fq_name + 9;
  4230.     } else {
  4231.         return(NULL);
  4232.     }
  4233.     }
  4234.  
  4235.     if(given_version != version[0])
  4236.       return(NULL);
  4237.  
  4238.     parsed_name=(struct carmel_mb_name *)fs_get(sizeof(struct carmel_mb_name));
  4239.     parsed_name->version[0] = version[0];
  4240.     parsed_name->version[1] = version[1];
  4241.  
  4242.     /*---- Find second # if there is one ---*/
  4243.     for(q = p; *q && *q != CARMEL_NAME_CHAR; q++);
  4244.  
  4245.     if(*q == CARMEL_NAME_CHAR) {
  4246.         /*----- There is a user name -----*/
  4247.         parsed_name->user = fs_get((q - p) + 1);
  4248.         strncpy(parsed_name->user, p, q - p);
  4249.         parsed_name->user[q - p] = '\0';
  4250.         p = q + 1;
  4251.     } else {
  4252.         parsed_name->user = NULL;
  4253.     }
  4254.  
  4255.     parsed_name->mailbox = cpystr(p);
  4256.  
  4257.     return(parsed_name);
  4258. }
  4259.  
  4260.  
  4261. void
  4262. carmel_free_mb_name(mb_name)
  4263.      struct carmel_mb_name *mb_name;
  4264. {
  4265.     if(mb_name->user != NULL)
  4266.       fs_give((void **)(&(mb_name->user)));
  4267.     fs_give((void **)(&(mb_name->mailbox)));
  4268.     fs_give((void **)&mb_name);
  4269. }
  4270.     
  4271.     
  4272.  
  4273.     
  4274.     
  4275.         
  4276.         
  4277.     
  4278.  
  4279.     
  4280. /*--------------------------------------------------
  4281.      A case insensitive strcmp()     
  4282.   
  4283.    Args: o, r -- The two strings to compare
  4284.  
  4285.  Result: integer indicating which is greater
  4286.   ---*/
  4287. strucmp2(o, r)
  4288.      register char *r, *o;
  4289. {
  4290.     if(r == NULL && o == NULL)
  4291.       return(0);
  4292.     if(o == NULL)
  4293.       return(1);
  4294.     if(r == NULL)
  4295.       return(-1);
  4296.  
  4297.     while(*o && *r && (isupper(*o) ? tolower(*o) : *o) ==
  4298.                       (isupper(*r) ? tolower(*r) : *r)) {
  4299.         o++, r++;
  4300.     }
  4301.     return((isupper(*o) ? tolower(*o): *o)-(isupper(*r) ? tolower(*r) : *r));
  4302. }
  4303.  
  4304.  
  4305.  
  4306. /*----------------------------------------------------------------------
  4307.      A case insensitive strncmp()     
  4308.   
  4309.    Args: o, r -- The two strings to compare
  4310.          n    -- length to stop comparing strings at
  4311.  
  4312.  Result: integer indicating which is greater
  4313.    
  4314.   ----*/
  4315. struncmp2(o, r, n)
  4316.      register char *r, *o;
  4317.      register int   n;
  4318. {
  4319.     if(r == NULL && o == NULL)
  4320.       return(0);
  4321.     if(o == NULL)
  4322.       return(1);
  4323.     if(r == NULL)
  4324.       return(-1);
  4325.  
  4326.     n--;
  4327.     while(n && *o && *r &&
  4328.           (isupper(*o)? tolower(*o): *o) == (isupper(*r)? tolower(*r): *r)) {
  4329.         o++; r++; n--;
  4330.     }
  4331.     return((isupper(*o)? tolower(*o): *o) - (isupper(*r)? tolower(*r): *r));
  4332. }
  4333.  
  4334. /*----------------------------------------------------------------------
  4335.     A replacement for strchr or index ...
  4336.  
  4337.  ....so we don't have to worry if it's there or not. We bring our own.
  4338. If we really care about efficiency and think the local one is more
  4339. efficient the local one can be used, but most of the things that take
  4340. a long time are in the c-client and not in pine.
  4341.  ----*/
  4342. char *
  4343. strindex2(buffer, ch)
  4344.     char *buffer;
  4345.     int ch;
  4346. {
  4347.     /** Returns a pointer to the first occurance of the character
  4348.         'ch' in the specified string or NULL if it doesn't occur **/
  4349.  
  4350.     do
  4351.       if(*buffer == ch)
  4352.         return(buffer);
  4353.     while (*buffer++ != '\0');
  4354.  
  4355.     return(NULL);
  4356. }
  4357.  
  4358.  
  4359. /*======================================================================
  4360.  */
  4361.  
  4362. xopen(file, mode)
  4363.      char *file, *mode;
  4364. {}
  4365.  
  4366.  
  4367. xclose(stream)
  4368.      FILE *stream;
  4369. {}
  4370.  
  4371. xread() {}
  4372.  
  4373. xwrite() {}
  4374.  
  4375. xlseek() {}
  4376.  
  4377. #ifdef BWC
  4378. carmel_match_glyph_wide(string)
  4379.      char *string;
  4380. {
  4381.     extern char *ptspecials;
  4382.     char *s;
  4383.  
  4384.     if(struncmp2(string, "content-type", 12))
  4385.       return(0);  /* Nope */
  4386.     string += 12;
  4387.     while(*string && isspace(*string)) string++;
  4388.     if(*string != ':')
  4389.       return(0);
  4390.     string++;
  4391.     while(*string && isspace(*string)) string++;
  4392.     s = string;
  4393.     string = rfc822_parse_word(string, ptspecials);
  4394.     if(string == NULL)
  4395.       return(0);
  4396.     if(struncmp2(s, "text", 4))
  4397.       return(0);
  4398.     while(*string && isspace(*string)) string++;
  4399.     if(*string != '/')
  4400.       return;
  4401.     string++;
  4402.     while(*string && isspace(*string)) string++;
  4403.     s = string;
  4404.     string = rfc822_parse_word(string, ptspecials);
  4405.     if(string == NULL)
  4406.       return(0);
  4407.     if(struncmp2(s, "x-bwc-glyph-wide", 16))
  4408.       return(0);
  4409.     return(1);
  4410. }
  4411. #endif    
  4412.     
  4413.